Tabla de contenido
- 1 Visualización interactiva de datos geográficos
- 2 Modelo predictivo con series de tiempo
- 3 Errores comunes al crear visualizaciones interactivas
- 4 Importación y cargue de datos en PostgreSQL usando Heroku
Visualización interactiva de datos geográficos¶
Objetivos
Utilizar mapas coropléticos para representar datos en distintas regiones geográficas
Generar mapas coropléticos interactivos, incluyendo mapas que representan países en el mundo y mapas que representan estados en EE.UU., haciendo cambios de diseño para añadir funcionalidad/estética, y añadiendo animaciones
Generar gráficos de dispersión interactivos en los mapas, incluidos los gráficos de dispersión que indican las geolocalizaciones de lugares de interés y gráficos de burbujas interactivos en mapas
Generar gráficos lineales interactivos en mapas, incluyendo aquellos que indiquen trayectorias en un mapa
En este capítulo, aprenderemos a utilizar visualizaciones interactivas para representar datos en regiones geográficas.
Introducción¶
La mayoría de los conjuntos de datos generados en el mundo actual incluyen algunas características que representan aspectos espaciales o aspectos geográficos. Por ejemplo, los usuarios de las plataformas de medios sociales se caracterizan por las diferentes partes del mundo en las que residen, las métricas de desarrollo mundial se se calculan para diferentes países del mundo, las rutas de transporte que abarcan diferentes lugares del planeta, etc. Es esencial aprender aprender formas sistemáticas de entender y presentar esa información de forma digerible y a la vez perspicaz.
Esta sección le ayudará a desarrollar esta capacidad proporcionando las herramientas necesarias para generar una variedad de gráficos que representen datos geográficos. Mientras que
altairygeopandasofrecen interesantes posibilidades para visualizar datos geográficos datos geográficos,plotlyes especialmente bueno para generar una variedad de gráficos geográficos que son fáciles de construir, depurar y personalizar. Por lo tanto, en este capítulo, utilizaremos plotly para demostrar la generación de diferentes clases de gráficos geográficos con múltiples conjuntos de datos disponibles públicamente de una variedad de contextos.
Mapas coropléticos¶
Un mapa coroplético es un mapa de una región con diferentes divisiones coloreadas para indicar el valor de una característica específica en esa división. Esta división puede ser un país, un estado, un distrito o cualquier otra zona bien documentada. Por ejemplo, se puede visualizar la población de un país en un mapa mundial, la población de un estado en un mapa de un país o el porcentaje de población con acceso a una tecnología con un mapa coropléjico.
Ejercicio 45: Creación de un mapa coroplético¶
En la primera visualización de esta sección, vamos a utilizar las estadísticas de uso de Internet publicadas en Our World in Data (https://ourworldindata.org/internet) y presentar el porcentaje de la población que utiliza Internet en cada país desde 1990 hasta 2017. ¿Se ha fijado en la característica llamada Código en el conjunto de datos? Se trata de un código asignado a cada país por una norma llamada ISO 3166-1. Se utiliza ampliamente para que los desarrolladores de todo el mundo tengan una forma común de referirse y acceder a los nombres de los países en cualquier dato.
En este ejercicio, generaremos un mapa coroplético mundial utilizando el conjunto de datos Our World in Datos. Como el DataFrame contiene registros de varios años, primero vamos a subdividir los datos en un año específico, por ejemplo, 2016. A continuación, utilizaremos este subconjunto para generar un mapa mundial. Para ello, vamos a seguir los siguientes pasos
Importar los módulos de Python
import pandas as pd
Lea los datos del archivo
.csv
internet_usage_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/share-of-individuals-using-the-internet.csv"
internet_usage_df = pd.read_csv(internet_usage_url)
Hacer sub-conjuntos de datos de un año específico, ya que el DataFrame contiene registros de múltiples años
internet_usage_2016 = internet_usage_df.query("Year==2016")
internet_usage_2016.head()
| Country | Code | Year | Individuals using the Internet (% of population) | |
|---|---|---|---|---|
| 16 | Afghanistan | AFG | 2016 | 10.595726 |
| 39 | Albania | ALB | 2016 | 66.363445 |
| 63 | Algeria | DZA | 2016 | 42.945527 |
| 85 | Andorra | AND | 2016 | 97.930637 |
| 107 | Angola | AGO | 2016 | 13.000000 |
Para los siguientes pasos, vamos a utilizar el módulo express (por su simplicidad) de
plotlyy utilizar la funciónchoroplethdel módulo. El primer argumento que se pasa a esta función es el DataFrame que queremos visualizar. Los siguientes parámetros:locations: Se establece como el nombre de la columna del DataFrame que contiene los códigos de país ISO 3166
color: Se establece el nombre de la columna que contiene la característica numérica con la que se va a colorear el mapa
hover_name: Se establece el nombre de la columna que contiene la característica que se mostrará al pasar el ratón por encima del mapa
color_continuous_scale: Esto se establece en un esquema de color, como: Blues | Reds | Greens | px.colors.sequential.Plasma.
Genera un mapa coroplético mundial interactivo utilizando la función choropleth de la biblioteca
plotly
import plotly.express as px
from IPython.display import IFrame, display, HTML
fig = px.choropleth(internet_usage_2016, locations="Code", # colunm containing ISO 3166 country codes
color = "Individuals using the Internet (% of population)", # column by which to color-code
hover_name = "Country", # column to display in hover information
color_continuous_scale = px.colors.sequential.Plasma)
# fig.show(renderer="svg", width=900, height=410)
fig.write_html('figures/figure1.html', auto_open=False)
IFrame('figures/figure1.html', width=900, height=415)
# display(HTML('figures/figure1.html'))
Observemos el gráfico con atención y veamos si las observaciones coinciden con nuestros conocimientos generales. Como era de esperar, el uso de Internet en el mundo occidental es mayor que en que en el este.
Es interesante ver en la Figura que un mayor porcentaje de la población (\(\approx91,6\)) en Australia y Canadá tiene acceso a Internet en comparación con Estados Unidos y la mayoría de los países europeos (\(\approx\)59,5).
Ejercicio 46: Ajustar un mapa coroplético mundial¶
En este ejercicio, haremos algunos cambios sencillos en el diseño del mapa coropleto, como cambiar la proyección del mapa de flat a natural earth, hacer zoom en una región específica, añadir texto al mapa utilizando la función
update_layout, y añadir una función de rotación.
Importar los módulos de Python
import pandas as pd
Lea los datos del archivo
.csv
internet_usage_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/share-of-individuals-using-the-internet.csv"
internet_usage_df = pd.read_csv(internet_usage_url)
Hacer sub-conjuntos de datos de un año específico, ya que el DataFrame contiene registros de múltiples años
internet_usage_2016 = internet_usage_df.query("Year==2016")
internet_usage_2016.head()
| Country | Code | Year | Individuals using the Internet (% of population) | |
|---|---|---|---|---|
| 16 | Afghanistan | AFG | 2016 | 10.595726 |
| 39 | Albania | ALB | 2016 | 66.363445 |
| 63 | Algeria | DZA | 2016 | 42.945527 |
| 85 | Andorra | AND | 2016 | 97.930637 |
| 107 | Angola | AGO | 2016 | 13.000000 |
Añade el texto del título al mapa coropleto estableciendo el parámetro
title_text
import plotly.express as px
fig = px.choropleth(internet_usage_2016,
locations="Code",
color="Individuals using the Internet (% of population)", # column by which to color-code
hover_name="Country", # column to display in hover information
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet usage across the world (% population)'
)
fig.show(renderer="svg")
Digamos que sólo nos interesa ver el uso de Internet en el continente de Sur América. Establezca
geo_scopea south america en la funciónupdate_layoutpara hacer zoom en la región de asia. Podemos hacerlo rápidamente con el siguiente código
import plotly.express as px
fig = px.choropleth(internet_usage_2016,
locations="Code",
color="Individuals using the Internet (% of population)", # column by which to color-code
hover_name="Country", # column to display in hover information
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Uso de Internet en todo Sur América (% de población) - 2016',
geo_scope = 'south america' # can be set to north america | south america | africa | asia | europe | usa
)
fig.show()
import plotly.express as px
fig = px.choropleth(internet_usage_2016,
locations="Code",
color="Individuals using the Internet (% of population)", # column by which to color-code
hover_name="Country", # column to display in hover information
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet usage across the Asian Continent (% population) - 2016',
geo_scope = 'asia' # can be set to north america | south america | africa | asia | europe | usa
)
fig.show()
Establece projection type como natural earth. La gráfica podría rotarse como un globo terráqueo real
import plotly.express as px
fig = px.choropleth(internet_usage_2016,
locations="Code",
color="Individuals using the Internet (% of population)", # column by which to color-code
hover_name="Country", # column to display in hover information
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet usage across the world (% population)',
# set projection style for the plot
geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
Prueba a arrastrar el mapa ahora. La rotación le da al gráfico un toque mucho más realista.
plotlyofrece muchas opciones de este tipo para ajustar las visualizaciones. Para experimentar con otros estilos de proyección aparte de los que hemos visto en nuestros ejemplos, visite la documentación oficial de documentación deplotlyaquí: https://plot.ly/python/reference/#layout-geo- proyección.
Ejercicio 47: Añadir animación a un mapa coroplético¶
En este ejercicio, animaremos un mapa coroplético mundial. Primero, elegiremos una columna. A continuación, añadiremos un componente deslizante al mapa para ver los registros en diferentes puntos temporales
Importar los módulos de Python
import pandas as pd
Lea los datos del archivo
.csv
internet_usage_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/share-of-individuals-using-the-internet.csv"
internet_usage_df = pd.read_csv(internet_usage_url)
Añade una animación a la columna year utilizando
animation_frame=year
import plotly.express as px
fig = px.choropleth(internet_usage_df, locations="Code",
color="Individuals using the Internet (% of population)", # lifeExp is a column of gapminder
hover_name="Country", # column to add to hover information
animation_frame="Year", # column on which to animate
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet usage across the world (% population)',
# set projection style for the plot
geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
Observe que el primer argumento de nuestra función de coropleta es el
DataFrameinternet_usage_dfDataFrame, que contiene registros para todos los años entre 1970- 2017, y nointernet_usage_2016, que habíamos utilizado hasta ahora. Si Si utilizáramos elDataFrameinternet_usage_2016, obtendríamos un gráfico estático sin deslizante, ya que no habría nada que animar con registros de un solo año. La funcionalidad de la animación y el deslizador es una forma sencilla de ver cómo ha crecido el uso de Internet en diferentes países del mundo a lo largo de los años.Sin embargo, ¡hay algo en el deslizador que resulta curioso! Los años en el deslizador no están en el orden correcto, comienza con 1990, luego llega hasta 2015, y luego vuelve a 1970 y así sucesivamente. La forma más fácil de solucionar este problema es ordenar el
DataFramepor tiempo (la variable Year).
Ordene el conjunto de datos por Year utilizando el siguiente código
internet_usage_df.sort_values(by=["Year"],inplace=True)
internet_usage_df.head()
| Country | Code | Year | Individuals using the Internet (% of population) | |
|---|---|---|---|---|
| 5347 | Syrian Arab Republic | NaN | 1960 | 0.0 |
| 718 | Burundi | BDI | 1960 | 0.0 |
| 5493 | Togo | TGO | 1960 | 0.0 |
| 572 | Botswana | BWA | 1960 | 0.0 |
| 3414 | Maldives | MDV | 1960 | 0.0 |
Vuelva a generar el gráfico animado ahora que la clasificación está hecha
import plotly.express as px
fig = px.choropleth(internet_usage_df, locations="Code",
color="Individuals using the Internet (% of population)", # lifeExp is a column of gapminder
hover_name="Country", # column to add to hover information
animation_frame="Year", # column on which to animate
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet usage across the world (% population)',
# set projection style for the plot
geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
Podemos ver que hubo definitivamente un aumento en el uso de Internet entre 1992 y 2010. Hay un punto más que debe ser abordado antes de cerrar nuestra discusión sobre mapas coropléticos mundiales. En su trabajo, es posible que se encuentre con conjuntos de datos que serían interesantes de visualizar en un mapa geográfico pero que no tienen una columna que indique su código ISO 3166-1. En estos casos, puede descargar los códigos de los países desde el sitio web oficial de la ISO: https://www.iban.com/country-codes.
Podrá ver el conjunto de datos de códigos de países utilizando el siguiente código
import pandas as pd
country_codes_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/country_codes.tsv"
country_codes = pd.read_csv(country_codes_url, sep='\t')
country_codes.head()
| Country | Alpha-2 code | Alpha-3 code | Numeric | |
|---|---|---|---|---|
| 0 | Afghanistan | AF | AFG | 4 |
| 1 | Albania | AL | ALB | 8 |
| 2 | Algeria | DZ | DZA | 12 |
| 3 | American Samoa | AS | ASM | 16 |
| 4 | Andorra | AD | AND | 20 |
Mapas de los estados de EE.UU¶
Aunque el objetivo de muchas visualizaciones es comparar y contrastar características específicas entre países, a menudo también hay contextos en los que necesitamos analizar características entre regiones más pequeñas, como los estados dentro de un país. Para generar mapas coropléticos para los estados de EE.UU. utilizaremos los datos de población por estados que están disponibles en el sitio web del censo de EE.UU. https://www.census.gov/newsroom/press-kits/2018/pop-estimates-national-state.html
Ejercicio 48: Creación de un mapa coroplético de Estados Unidos¶
En este ejercicio, utilizaremos el conjunto de datos de población de los Estados Unidos. Vamos a modificar el conjunto de datos y lo utilizaremos para trazar un mapa de coroplético de todo el estado. A continuación, cambiaremos el diseño de este mapa para mostrar la población de EE.UU. a través de los estados
Importar el módulo de Python
import pandas as pd
Leer el conjunto de datos de la URL
us_population_url = 'https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/us_state_population.tsv'
df = pd.read_csv(us_population_url, sep='\t')
df.head()
| State | Code | 2010 | 2011 | 2012 | 2013 | 2014 | 2015 | 2016 | 2017 | 2018 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | Alabama | AL | 4785448 | 4798834 | 4815564 | 4830460 | 4842481 | 4853160 | 4864745 | 4875120 | 4887871 |
| 1 | Alaska | AK | 713906 | 722038 | 730399 | 737045 | 736307 | 737547 | 741504 | 739786 | 737438 |
| 2 | Arizona | AZ | 6407774 | 6473497 | 6556629 | 6634999 | 6733840 | 6833596 | 6945452 | 7048876 | 7171646 |
| 3 | Arkansas | AR | 2921978 | 2940407 | 2952109 | 2959549 | 2967726 | 2978407 | 2990410 | 3002997 | 3013825 |
| 4 | California | CA | 37320903 | 37641823 | 37960782 | 38280824 | 38625139 | 38953142 | 39209127 | 39399349 | 39557045 |
Es bueno que este conjunto de datos también tenga los códigos estatales disponibles en la función Código. Sin embargo, los datos no están en el formato que querríamos: están en el formato ancho y necesitamos que sea largo
Utilice la función
meltpara convertir los datos al formato deseado
df = pd.melt(df, id_vars=['State', 'Code'], var_name="Year", value_name="Population")
df.head()
| State | Code | Year | Population | |
|---|---|---|---|---|
| 0 | Alabama | AL | 2010 | 4785448 |
| 1 | Alaska | AK | 2010 | 713906 |
| 2 | Arizona | AZ | 2010 | 6407774 |
| 3 | Arkansas | AR | 2010 | 2921978 |
| 4 | California | CA | 2010 | 37320903 |
Una vez que sepa cómo generar un mapa coroplético de los países del mundo, un mapa coroplético de los estados de EE.UU. es bastante sencillo. A diferencia del caso de generar un mapa coroplético mundial en el que utilizamos el módulo
plotlyexpress utilizaremos el módulograph_objectspara generar el mapa coroplético para los estados en los Estados UnidosImportar el módulo graph_objects
import plotly.graph_objects as go
Inicialice la figura con la función
Figureengraph_objects. Específicamente, el argumento de datos debe ser una instancia de la claseChoroplethcon los siguientes parámetroslocations: Se establece en la columna del
DataFrameque contiene los códigos de los nombres de los estados. los códigos de los nombresz: Se establece en la columna que contiene la característica numérica con la que el mapa debe ser codificado por colores
locationmode: Este se establece como Estados Unidos.
colorscale: Esto se establece en un esquema de colores, como Blues | Reds | Greens. Para más opciones, consulte la documentación oficial de
plotly: https://plot.ly/python/referencia/.colorbar_title: Esto se establece en el título de la barra de color de la derecha, indicando la correspondencia de los valores de color y característica.
# initialize the figure
fig = go.Figure(
data=go.Choropleth(
locations=df['Code'], # Code for US states
z = df['Population'].astype(int), # Data to be color-coded
locationmode = 'USA-states', # set of locations match entries in 'locations'
colorscale = 'Blues',
colorbar_title = "Population"
)
)
Realice cambios en el diseño con
update_layout()establezcatitle_textygeo_scope
# update layout
fig.update_layout(
title_text = 'US Population across states',
geo_scope='usa', # limit map scope to USA
)
fig.show()
Los mapas coropléticos son una forma eficaz de visualizar estadísticas agregadas en las divisiones de una región geográfica. Se pueden utilizar dos módulos de
plotlyexpress ygraph_objectspara generar mapas coropléticos interactivos. Los módulos mapean registros de de divisiones, como países y estados, a ubicaciones en mapas geográficos utilizando un sistema de nombres de códigos estandarizados de países y estados
Gráficos sobre mapas geográficos¶
Mientras que los gráficos anteriores eran excelentes para visualizar tendencias más globales como países o estados, ¿qué pasa si queremos representar características en regiones más pequeñas, digamos dentro de los estados individuales? En esta sección, aprenderá a dibujar gráficos de dispersión y gráficos de burbujas en los mapas.
Ejercicio 49: Creación de un gráfico de dispersión en un mapa geográfico¶
Vamos a trazar las ubicaciones de las tiendas Walmart en un mapa de los Estados Unidos. Este conjunto de datos está disponible públicamente en: https://github.com/plotly/datasets/ sitio web de
plotly. En este ejercicio, utilizaremos el conjunto de datos de aperturas de tiendas Walmart de 1962 a 2006.Para crear un gráfico de dispersión a partir de este conjunto de datos, utilizaremos el módulo
graph_objects. Encontraremos una ubicación de interés en el mapa y asignaremos longitudes y latitudes en ese mapa y averiguaremos el número de aperturas de tiendas Walmart para diferentes partes de los Estados Unidos.
Importar módulos de Python
import pandas as pd
Lee los datos de la URL
walmart_locations_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/1962_2006_walmart_store_openings.csv"
walmart_loc_df = pd.read_csv(walmart_locations_url)
walmart_loc_df.head()
| storenum | OPENDATE | date_super | conversion | st | county | STREETADDR | STRCITY | STRSTATE | ZIPCODE | type_store | LAT | LON | MONTH | DAY | YEAR | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 7/1/62 | 3/1/97 | 1.0 | 5 | 7 | 2110 WEST WALNUT | Rogers | AR | 72756 | Supercenter | 36.342235 | -94.07141 | 7 | 1 | 1962 |
| 1 | 2 | 8/1/64 | 3/1/96 | 1.0 | 5 | 9 | 1417 HWY 62/65 N | Harrison | AR | 72601 | Supercenter | 36.236984 | -93.09345 | 8 | 1 | 1964 |
| 2 | 4 | 8/1/65 | 3/1/02 | 1.0 | 5 | 7 | 2901 HWY 412 EAST | Siloam Springs | AR | 72761 | Supercenter | 36.179905 | -94.50208 | 8 | 1 | 1965 |
| 3 | 8 | 10/1/67 | 3/1/93 | 1.0 | 5 | 29 | 1621 NORTH BUSINESS 9 | Morrilton | AR | 72110 | Supercenter | 35.156491 | -92.75858 | 10 | 1 | 1967 |
| 4 | 7 | 10/1/67 | NaN | NaN | 5 | 119 | 3801 CAMP ROBINSON RD. | North Little Rock | AR | 72118 | Wal-Mart | 34.813269 | -92.30229 | 10 | 1 | 1967 |
Volveremos a utilizar el módulo
graph_objectspara generar nuestro gráfico de dispersión en el mapa de Estados Unidos. En cuanto al mapa de coropletas, utilizaremos la funciónFiguredegraph_objectsy la funciónupdate_layout(). Sin embargo, esta vez asignaremos una instancia de la claseScattergeocomo argumento deFigure(). Pasaremos las longitudes y latitudes de nuestras localizaciones de interés utilizando los parámetroslonylat
Trazar el gráfico de dispersión utilizando la función
update_layout
import plotly.graph_objects as go
fig = go.Figure(data=go.Scattergeo(
lon = walmart_loc_df['LON'], # column containing longitude information of the locations to plot
lat = walmart_loc_df['LAT'], # column containing latitude information of the locations to plot
text = walmart_loc_df['STREETADDR'], # column containing value to be displayed on hovering over the map
mode = 'markers' # a marker for each location
))
fig.update_layout(
title = 'Walmart stores across world',
geo_scope='usa',
)
fig.show()
Una observación sorprendente es que Walmart es mucho más prominente en el este de los EE.UU. que en el oeste
Ejercicio 50: Creación de un gráfico de burbujas en un mapa geográfico¶
Dado que la parte oriental del mapa de EE.UU. parece estar muy densamente poblada de tiendas Walmart, podría ser una buena idea mostrar una característica agregada, como el recuento de tiendas Walmart en los diferentes estados. Los gráficos de burbujas están diseñados exactamente para este tipo de visualización. En el contexto actual de la visualización de datos geográficos, los gráficos de burbujas son gráficos con tantas burbujas como regiones de interés, en los que el tamaño de las burbujas depende del valor que indiquen, cuanto mayor sea el valor, mayor será la burbuja
En este ejercicio, utilizaremos el conjunto de datos de aperturas de tiendas Walmart de 1962 a 2006 y generar un gráfico de burbujas para ver el número de tiendas Walmart en los diferentes estados de los Estados Unidos. A continuación, veremos otro contexto y generaremos un gráfico de burbujas utilizando el conjunto de datos Internet_usage para averiguar el número de usuarios de Internet en todo el mundo. También animaremos el gráfico de burbujas para mostrar el aumento del número de usuarios de Internet en todo el mundo
Importar módulos de Python
import pandas as pd
Lee los datos de la URL
walmart_locations_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/1962_2006_walmart_store_openings.csv"
walmart_loc_df = pd.read_csv(walmart_locations_url)
walmart_loc_df.head()
| storenum | OPENDATE | date_super | conversion | st | county | STREETADDR | STRCITY | STRSTATE | ZIPCODE | type_store | LAT | LON | MONTH | DAY | YEAR | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 7/1/62 | 3/1/97 | 1.0 | 5 | 7 | 2110 WEST WALNUT | Rogers | AR | 72756 | Supercenter | 36.342235 | -94.07141 | 7 | 1 | 1962 |
| 1 | 2 | 8/1/64 | 3/1/96 | 1.0 | 5 | 9 | 1417 HWY 62/65 N | Harrison | AR | 72601 | Supercenter | 36.236984 | -93.09345 | 8 | 1 | 1964 |
| 2 | 4 | 8/1/65 | 3/1/02 | 1.0 | 5 | 7 | 2901 HWY 412 EAST | Siloam Springs | AR | 72761 | Supercenter | 36.179905 | -94.50208 | 8 | 1 | 1965 |
| 3 | 8 | 10/1/67 | 3/1/93 | 1.0 | 5 | 29 | 1621 NORTH BUSINESS 9 | Morrilton | AR | 72110 | Supercenter | 35.156491 | -92.75858 | 10 | 1 | 1967 |
| 4 | 7 | 10/1/67 | NaN | NaN | 5 | 119 | 3801 CAMP ROBINSON RD. | North Little Rock | AR | 72118 | Wal-Mart | 34.813269 | -92.30229 | 10 | 1 | 1967 |
Utilice la función
groupbypara calcular el número de tiendas Walmart por estado
walmart_stores_by_state = walmart_loc_df.groupby('STRSTATE').count()['storenum'].reset_index().rename(columns={'storenum':'NUM_STORES'})
walmart_stores_by_state.head()
| STRSTATE | NUM_STORES | |
|---|---|---|
| 0 | AL | 90 |
| 1 | AR | 81 |
| 2 | AZ | 55 |
| 3 | CA | 159 |
| 4 | CO | 56 |
Para generar los gráficos de burbujas, utilizaremos el módulo
plotly expressy la funciónscatter_geo. Obsérvese que el parámetrolocationsse establece con el nombre de la columna que contiene los códigos de estado, y el parámetrosizese ajusta a la función funciónNUM_STORES
import plotly.express as px
fig = px.scatter_geo(walmart_stores_by_state,
locations="STRSTATE", # name of column which contains state codes
size="NUM_STORES", # name of column which contains aggregate value to visualize
locationmode = 'USA-states',
hover_name="STRSTATE",
size_max=45)
fig.update_layout(
# add a title text for the plot
title_text = 'Walmart stores across states in the US',
# limit plot scope to USA
geo_scope='usa'
)
fig.show()
¿Se te ocurre algún otro contexto en el que un gráfico de burbujas pueda ser útil para visualización? ¿Qué tal si revisamos los datos de uso de Internet (sobre los porcentajes de la población que utiliza Internet en cada país) para generar un gráfico de burbujas a nivel mundial?
Resulta que el recuento de individuos que utilizan Internet en cada país también está disponible en el mismo recurso (Our World in Data: https://ourworldindata. org/internet) que hemos utilizado para recoger nuestros datos anteriores.
Utilice el siguiente código para leer los datos de los usuarios de Internet por país conjunto de datos
import pandas as pd
internet_users_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/number-of-internet-users-by-country.csv"
internet_users_df = pd.read_csv(internet_users_url)
internet_users_df.head()
| Country | Code | Year | Number of internet users (users) | |
|---|---|---|---|---|
| 0 | Afghanistan | AFG | 1990 | 0 |
| 1 | Afghanistan | AFG | 2001 | 990 |
| 2 | Afghanistan | AFG | 2002 | 1003 |
| 3 | Afghanistan | AFG | 2003 | 20272 |
| 4 | Afghanistan | AFG | 2004 | 25520 |
Ordenar el DataFrame por la característica Year
internet_users_df.sort_values(by=['Year'],inplace=True)
internet_users_df.head()
| Country | Code | Year | Number of internet users (users) | |
|---|---|---|---|---|
| 0 | Afghanistan | AFG | 1990 | 0 |
| 1257 | Eritrea | ERI | 1990 | 0 |
| 1236 | Equatorial Guinea | GNQ | 1990 | 0 |
| 4016 | Timor | TLS | 1990 | 0 |
| 1214 | El Salvador | SLV | 1990 | 0 |
Traza el número de usuarios que utilizan Internet en todo el mundo en 2016
import plotly.express as px
fig = px.scatter_geo(internet_users_df.query("Year==2016"),
locations="Code", # name of column indicating country-codes
size="Number of internet users (users)", # name of column by which to size the bubble
hover_name="Country", # name of column to be displayed while hovering over the map
size_max=80, # parameter to scale all bubble sizes
color_continuous_scale=px.colors.sequential.Plasma)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet users across the world - 2016', # set projection style for the plot
geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
¿Notas que el mayor número de usuarios procede de India y China? Como sabemos por nuestro conjunto de datos anterior que el porcentaje de la población que utiliza Internet en estos países es bajo, este gran grupo de usuarios puede atribuirse a la población de estos países.
Anima el gráfico de burbujas para mostrar el aumento del número de usuarios de Internet a lo largo de los años utilizando el parámetro
animation_frame:
import plotly.express as px
fig = px.scatter_geo(internet_users_df,
locations="Code", # name of column indicating country-codes
size="Number of internet users (users)", # name of column by which to size the bubble
hover_name="Country", # name of column to be displayed while hovering over the map
size_max=80, # parameter to scale all bubble size
animation_frame="Year"
)
fig.update_layout(
# add a title text for the plot
title_text = 'Internet users across the world', # set projection style for the plot
geo = dict(projection={'type':'natural earth'}) # by default, projection type is set to 'equirectangular'
)
fig.show()
Podemos ver en los dos gráficos anteriores cómo el número de usuarios de Internet aumentó entre 2001 y 2016 en Estados Unidos. Los gráficos de dispersión en los mapas pueden utilizarse para mostrar ubicaciones específicas de interés en los mapas geográficos, mientras que los mapas de burbujas son una buena forma de presentar los datos de recuento a través de diferentes fuentes.
Líneas sobre mapas geográficos¶
Los gráficos de líneas representados en mapas son otra clase importante de visualización de datos geográficos. Para esta sección, utilizaremos los datos de aeropuertos y vuelos del conjunto de datos de retrasos y cancelaciones de vuelos de 2015 publicado por el U.S. Department of Transportation’s (DOT). Bureau of Transportation Statistics
Como el conjunto de datos es enorme, solo incluiremos los datos de todos los vuelos con retrasos de las aerolíneas el 1 de enero de 2015.
airports.csv: Contiene atributos de ubicación como la información de latitud y longitud para todos los aeropuertos
new_year_day_2015_delayed_flights.csv: Contiene detalles de los vuelos, como los números de vuelo, aeropuertos de origen y destino de todos los vuelos del subconjunto seleccionado.
51: Creación de gráficos de líneas en un mapa geográfico¶
En este ejercicio, utilizaremos el conjunto de datos de los aeropuertos y generaremos primero un gráfico de dispersión para averiguar la ubicación de todos los aeropuertos de Estados Unidos. A continuación, combinaremos los dos
DataFrames(flights y airport_record) para obtener la longitud y latitud de los de los aeropuertos de origen de todos los vuelos y dibujaremos líneas desde el aeropuerto de origen hasta el aeropuerto de destino de cada vuelo utilizando esta fusión.
Cargue primero el conjunto de datos de los aeropuertos
import pandas as pd
us_airports_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/airports.csv"
us_airports_df = pd.read_csv(us_airports_url)
us_airports_df.head()
| IATA_CODE | AIRPORT | CITY | STATE | COUNTRY | LATITUDE | LONGITUDE | |
|---|---|---|---|---|---|---|---|
| 0 | ABE | Lehigh Valley International Airport | Allentown | PA | USA | 40.65236 | -75.44040 |
| 1 | ABI | Abilene Regional Airport | Abilene | TX | USA | 32.41132 | -99.68190 |
| 2 | ABQ | Albuquerque International Sunport | Albuquerque | NM | USA | 35.04022 | -106.60919 |
| 3 | ABR | Aberdeen Regional Airport | Aberdeen | SD | USA | 45.44906 | -98.42183 |
| 4 | ABY | Southwest Georgia Regional Airport | Albany | GA | USA | 31.53552 | -84.19447 |
Generar un gráfico de dispersión en el mapa de Estados Unidos para indicar la ubicación de todos los aeropuertos en nuestro conjunto de datos, utilizando el módulo
graph_objects
import plotly.graph_objects as go
fig = go.Figure()
fig.add_trace(go.Scattergeo(locationmode = 'USA-states',
lon = us_airports_df['LONGITUDE'],
lat = us_airports_df['LATITUDE'],
hoverinfo = 'text',
text = us_airports_df['AIRPORT'],
mode = 'markers',
marker = dict(size = 5,color = 'black')))
fig.update_layout(
title_text = 'Airports in the USA',
showlegend = False,
geo = go.layout.Geo(scope = 'usa')
)
fig.show()
Al pasar el ratón por encima de un punto de datos, aparecerá el nombre del aeropuerto estadounidense. ¿Se ha dado cuenta de que hay una función
add_trace()además de la habitual creación de instancias de la claseScattergeo? La funciónadd_tracese utiliza porque vamos a superponer nuestros datos de vuelo en forma de líneas sobre este gráfico de dispersión en el mapa. La funciónadd_tracepermite aplotlytratar el gráfico de dispersión y los trazados de líneas como múltiples capas en el mapa.
Cargar el archivo que contiene los registros de vuelo
new_year_2015_flights_url = "https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/new_year_day_2015_delayed_flights.csv"
new_year_2015_flights_df = pd.read_csv(new_year_2015_flights_url)
new_year_2015_flights_df.head()
| YEAR | MONTH | DAY | DAY_OF_WEEK | AIRLINE | FLIGHT_NUMBER | TAIL_NUMBER | ORIGIN_AIRPORT | DESTINATION_AIRPORT | SCHEDULED_DEPARTURE | ... | ARRIVAL_TIME | ARRIVAL_DELAY | DIVERTED | CANCELLED | CANCELLATION_REASON | AIR_SYSTEM_DELAY | SECURITY_DELAY | AIRLINE_DELAY | LATE_AIRCRAFT_DELAY | WEATHER_DELAY | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2015 | 1 | 1 | 4 | HA | 17 | N389HA | LAS | HNL | 145 | ... | 610.0 | 15.0 | 0 | 0 | NaN | 0.0 | 0.0 | 15.0 | 0.0 | 0.0 |
| 1 | 2015 | 1 | 1 | 4 | B6 | 2134 | N307JB | SJU | MCO | 400 | ... | 730.0 | 85.0 | 0 | 0 | NaN | 0.0 | 0.0 | 85.0 | 0.0 | 0.0 |
| 2 | 2015 | 1 | 1 | 4 | B6 | 2276 | N646JB | SJU | BDL | 438 | ... | 908.0 | 89.0 | 0 | 0 | NaN | 17.0 | 0.0 | 72.0 | 0.0 | 0.0 |
| 3 | 2015 | 1 | 1 | 4 | US | 425 | N174US | PDX | PHX | 520 | ... | 950.0 | 60.0 | 0 | 0 | NaN | 0.0 | 0.0 | 60.0 | 0.0 | 0.0 |
| 4 | 2015 | 1 | 1 | 4 | AA | 89 | N3KVAA | IAH | MIA | 520 | ... | 935.0 | 54.0 | 0 | 0 | NaN | 0.0 | 0.0 | 54.0 | 0.0 | 0.0 |
5 rows × 31 columns
Junto con los aeropuertos de origen y destino de cada vuelo, necesitamos tener la información de longitud y latitud de los aeropuertos correspondientes. Para ello necesitamos fusionar los
DataFramesque contienen los datos del aeropuerto y del vuelo. Primero fusionamos los dos conjuntos de datos para obtener las longitudes y latitudes de los de todos los vuelos
# merge the DataFrames on origin airport codes
new_year_2015_flights_df = new_year_2015_flights_df.merge(us_airports_df[['IATA_CODE','LATITUDE','LONGITUDE']], \
left_on='ORIGIN_AIRPORT', \
right_on='IATA_CODE', \
how='inner')
# drop the duplicate column containing airport code
new_year_2015_flights_df.drop(columns=['IATA_CODE'],inplace=True)
# rename the latitude and longitude columns to reflect that they correspond to the origin airport
new_year_2015_flights_df.rename(columns={"LATITUDE":"ORIGIN_AIRPORT_LATITUDE", "LONGITUDE":"ORIGIN_AIRPORT_LONGITUDE"},inplace=True)
new_year_2015_flights_df.head()
| YEAR | MONTH | DAY | DAY_OF_WEEK | AIRLINE | FLIGHT_NUMBER | TAIL_NUMBER | ORIGIN_AIRPORT | DESTINATION_AIRPORT | SCHEDULED_DEPARTURE | ... | DIVERTED | CANCELLED | CANCELLATION_REASON | AIR_SYSTEM_DELAY | SECURITY_DELAY | AIRLINE_DELAY | LATE_AIRCRAFT_DELAY | WEATHER_DELAY | ORIGIN_AIRPORT_LATITUDE | ORIGIN_AIRPORT_LONGITUDE | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2015 | 1 | 1 | 4 | HA | 17 | N389HA | LAS | HNL | 145 | ... | 0 | 0 | NaN | 0.0 | 0.0 | 15.0 | 0.0 | 0.0 | 36.08036 | -115.15233 |
| 1 | 2015 | 1 | 1 | 4 | HA | 7 | N395HA | LAS | HNL | 900 | ... | 0 | 0 | NaN | 0.0 | 0.0 | 23.0 | 0.0 | 0.0 | 36.08036 | -115.15233 |
| 2 | 2015 | 1 | 1 | 4 | AA | 1623 | N438AA | LAS | DFW | 905 | ... | 0 | 0 | NaN | 4.0 | 0.0 | 133.0 | 0.0 | 0.0 | 36.08036 | -115.15233 |
| 3 | 2015 | 1 | 1 | 4 | DL | 1530 | N954DN | LAS | MSP | 920 | ... | 0 | 0 | NaN | 1.0 | 0.0 | 15.0 | 0.0 | 0.0 | 36.08036 | -115.15233 |
| 4 | 2015 | 1 | 1 | 4 | WN | 1170 | N902WN | LAS | ELP | 950 | ... | 0 | 0 | NaN | 5.0 | 0.0 | 11.0 | 0.0 | 0.0 | 36.08036 | -115.15233 |
5 rows × 33 columns
Ahora, realizaremos una fusión similar para obtener los datos de latitud y longitud de los aeropuertos de destino de todos los vuelos
# merge the DataFrames on destination airport codes
new_year_2015_flights_df = new_year_2015_flights_df.merge(us_airports_df[['IATA_CODE','LATITUDE','LONGITUDE']], \
left_on='DESTINATION_AIRPORT', \
right_on='IATA_CODE', \
how='inner')
# drop the duplicate column containing airport code
new_year_2015_flights_df.drop(columns=['IATA_CODE'],inplace=True)
# rename the latitude and longitude columns to reflect that they correspond to the destination airport
new_year_2015_flights_df.rename(columns={'LATITUDE':'DESTINATION_AIRPORT_LATITUDE', 'LONGITUDE':'DESTINATION_AIRPORT_LONGITUDE'},inplace=True)
new_year_2015_flights_df.head()
| YEAR | MONTH | DAY | DAY_OF_WEEK | AIRLINE | FLIGHT_NUMBER | TAIL_NUMBER | ORIGIN_AIRPORT | DESTINATION_AIRPORT | SCHEDULED_DEPARTURE | ... | CANCELLATION_REASON | AIR_SYSTEM_DELAY | SECURITY_DELAY | AIRLINE_DELAY | LATE_AIRCRAFT_DELAY | WEATHER_DELAY | ORIGIN_AIRPORT_LATITUDE | ORIGIN_AIRPORT_LONGITUDE | DESTINATION_AIRPORT_LATITUDE | DESTINATION_AIRPORT_LONGITUDE | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2015 | 1 | 1 | 4 | HA | 17 | N389HA | LAS | HNL | 145 | ... | NaN | 0.0 | 0.0 | 15.0 | 0.0 | 0.0 | 36.08036 | -115.15233 | 21.31869 | -157.92241 |
| 1 | 2015 | 1 | 1 | 4 | HA | 7 | N395HA | LAS | HNL | 900 | ... | NaN | 0.0 | 0.0 | 23.0 | 0.0 | 0.0 | 36.08036 | -115.15233 | 21.31869 | -157.92241 |
| 2 | 2015 | 1 | 1 | 4 | UA | 253 | N768UA | IAH | HNL | 920 | ... | NaN | 8.0 | 0.0 | 42.0 | 0.0 | 0.0 | 29.98047 | -95.33972 | 21.31869 | -157.92241 |
| 3 | 2015 | 1 | 1 | 4 | UA | 328 | N210UA | DEN | HNL | 1130 | ... | NaN | 0.0 | 0.0 | 31.0 | 0.0 | 0.0 | 39.85841 | -104.66700 | 21.31869 | -157.92241 |
| 4 | 2015 | 1 | 1 | 4 | UA | 1173 | N56859 | SFO | HNL | 805 | ... | NaN | 0.0 | 0.0 | 17.0 | 0.0 | 0.0 | 37.61900 | -122.37484 | 21.31869 | -157.92241 |
5 rows × 35 columns
Ahora, dibujaremos nuestros gráficos de líneas. Para cada vuelo, tenemos que dibujar una línea entre el aeropuerto de origen y el de destino. Esto se hace proporcionando los valores de latitud y longitud de los aeropuertos de destino y origen a los parámetros
lonylatdeScattergeoy ajustando el modo a líneas en lugar de marcadores. Además, fíjate en que aquí estamos utilizando otra funciónadd_trace
#Now, we will draw our line plots -- for each flight, we need to draw a line between the origin and destination airport. This is done by providing the latitude and longitude values of destination and origin airports to the lonand lat parameters of Scattergeo and setting mode to 'lines' instead of 'markers'. Also, notice that we are using another add_trace function here. It may take a few minutes for the plot to show the flight routes.
for i in range(len(new_year_2015_flights_df)):
fig.add_trace(
go.Scattergeo(
locationmode = 'USA-states',
lon = [new_year_2015_flights_df['ORIGIN_AIRPORT_LONGITUDE'][i], new_year_2015_flights_df['DESTINATION_AIRPORT_LONGITUDE'][i]],
lat = [new_year_2015_flights_df['ORIGIN_AIRPORT_LATITUDE'][i], new_year_2015_flights_df['DESTINATION_AIRPORT_LATITUDE'][i]],
mode = 'lines',
line = dict(width = 1,color = 'red')
)
)
fig.update_layout(
title_text = 'Delayed flight on Jan 1, 2015 in USA',
showlegend = False,
geo = go.layout.Geo(
scope = 'usa'
),
)
fig.show()
Los trazados de líneas en los mapas geográficos pueden ser generados usando el módulo
graph_objectsdeplotly.
Tarea 2.1¶
Trabajaremos con los conjuntos de datos Renewable Energy Consumption and Production de Our World in Data https://ourworldindata.org/renewable-energy). Estos conjuntos de datos están disponibles en el repositorio GitHub del curso, púedes ser importados con las URL: share-of-electricity-production-from-renewable-sources.csv, renewable-energy-consumption-by-country.csv
Su tarea consiste en crear mapas coropléticos de la producción y el consumo totales de energía renovable y el consumo en diferentes países del mundo animados en base a los años de años de producción/consumo entre (excluyendo) 2007 y 2017.
Pasos principales
Cargar el conjunto de datos de producción de energía renovable.
Ordenar el
DataFramede producción en función de la característica Year.Generar un mapa de coropletas para la producción de energía renovable utilizando el módulo
plotly expressanimado en función del Year.Actualice el diseño para incluir un estilo de proyección adecuado y un texto de título, y luego muestre la figura.
Cargue el conjunto de datos de consumo de energía renovable.
Convierta el
DataFramede consumo a un formato adecuado para su visualización.Ordenar el
DataFramede consumo en base a la característica Year.Generar un mapa coroplético para el consumo de energía renovable utilizando el módulo
plotly expressanimado en función del año.Actualice el diseño para incluir un estilo de proyección adecuado y un texto de título, y luego muestre la figura.
Resumen
En este capítulo hemos presentado tres tipos diferentes de visualización de datos geográficos, gráficos de dispersión y de burbujas sobre mapas geográficos y gráficos de líneas sobre mapas geográficos. Los mapas coropléticos presentan estadísticas agregadas a través de diferentes regiones de los mapas geográficos. Los gráficos de dispersión son eficaces para indicar detalles detalles sobre lugares específicos de interés, mientras que los gráficos de burbujas son útiles para presentar datos de recuento por región en un mapa. Los gráficos de líneas son útiles para visualizar las rutas de de los sistemas de transporte, por ejemplo. Estos gráficos pueden generarse fácilmente con los módulos plotly express y graph_objects. La animación puede realizarse con respecto a una característica numérica discreta en un conjunto de datos.
Modelo predictivo con series de tiempo¶
Definición: Un proceso estocástico es una colección o familia de variables aleatorias \(\{X_{t}\}_{t\in I}\) ordenadas según el subíndice \(t\) que en general se suele identificar con el tiempo. Llamamos trayectoria del proceso a una realización del proceso estocástico. Si \(I\) es discreto, el proceso es en tiempo discreto. Si \(I\) es continuo, el proceso es en tiempo continuo.
Observación:
Por tanto, para cada instante \(t\) tendremos una variable aleatoria distinta representada por \(X_{t}\), con lo que un proceso estocástico puede interpretarse como una sucesión de variables aleatorias cuyas características pueden variar a lo largo del tiempo.
Un ejemplo de proceso en tiempo discreto se obtiene para \(I = \{1,\dots,n\}\). En este caso, el proceso es \(Y_{1}, Y_{2},\dots, Y_{n}\), y una trayectoria se denota por \(y_{1}, y_{2},\dots, y_{n}\). Un ejemplo de proceso en tiempo continuo se obtiene para \(I=[0, T], I=[0,\infty], I=(-\infty,\infty)\)
Definición: Una serie temporal es una realización parcial de un proceso estocástico de parámetro tiempo discreto, donde los elementos de \(I\) están ordenados y corresponden a instantes equidistantes del tiempo.

Definición: Un proceso estocástico \((Y_{t})\) es estacionario cuando las propiedades estadísticas de cualquier secuencia finita \(Y_{t_{1}}, Y{t_{2}},\dots, Y{t_{n}}, (n\geq1)\) de componentes de \((Y_{t})\) son semejantes a las de la secuencia \(Y_{t_{1}+h}, Y_{t_{2}+h},\dots, Y_{t_{n}+h}\) para cualquier número entero \(h = \pm1, \pm2,\dots\)
Definición: Un proceso estocástico \((Y_{t})\) es no estacionario cuando las propiedades estadísticas de al menos una secuencia finita \(Y_{t_{1}}, Y{t_{2}},\dots, Y{t_{n}}, (n\geq1)\) de componentes de \((Y_{t})\), son diferentes de las de la secuencia \(Y_{t_{1}+h}, Y_{t_{2}+h},\dots, Y_{t_{n}+h}\) para al menos un número entero \(h>0\).
Definición: La covarianza entre \(y_{t}\) y su valor en otro periodo de tiempo, digamos, \(y_{t+k}\) se denomina autocovarianza en el retardo \(k\), y se define como $\(\gamma_{k}=Cov(y_{t}, y_{t+k})=E[(y_{t}-\mu)(y_{t+k}-\mu)]\)\( La colección de los valores de \)\gamma_{k}, k = 0, 1, 2,\dots\( se denomina función de autocovarianza. Obsérvese que la autocovarianza en el lag \)k = 0\( es simplemente la varianza de la serie temporal; es decir, \)\gamma_{0} = \sigma_{y}^{2}\(, que es constante para una serie temporal estacionaria. El coeficiente de autocorrelación en el lag \)k\( para una serie temporal estacionaria es \)\(\rho_{k}=\frac{E[(y_{t}-\mu)(y_{t+k}-\mu)]}{\sqrt{E[(y_{t}-\mu)^{2}]E[(y_{t+k}-\mu)^{2}]}}=\frac{Cov(y_{t}, y_{t+k})}{Var(y_{t})}=\frac{\gamma_{k}}{\gamma_{0}}\)\( La colección de los valores de \)\rho_{k}, k = 0, 1, 2,\dots$ se llama función de autocorrelación (ACF).
Definición: Un modelo para un proceso estocástico es cualquier conjunto de hipótesis bien definidas sobre las propiedades estadísticas de dicho proceso.
Introducción al modelo ARIMA¶
Los modelos ARIMA (Autorregresivo integrado de media móvil) aproximan los valores futuros de una serie temporal como una función lineal de observaciones pasadas y términos de ruido blanco. Una serie de tiempo \(y_{t}\) se llama un proceso de media móvil integrada autorregresiva (ARIMA) de órdenes \(p, d\), y \(q\), denotado ARIMA\((p, d, q)\) si su diferencia \(d\) da lugar a un proceso estacionario ARMA\((p, q)\). Por lo tanto, un ARIMA\((p, d, q)\) puede escribirse como
where $\(\Phi(B)=1-\sum_{i=1}^{p}\phi_{i}B^{i}\quad\text{and}\quad\Theta(B)=1-\sum_{i=1}^{q}\theta_{i}B^{i}\)$
son los términos del operador back-shit en los AR\((p)\) y MA\((q)\) definidos como \(\Phi(B)y_{t}=\delta+\varepsilon_{t}\) y \(y_{t}=\mu+\Theta(B)\varepsilon_{t}\), con \(\delta=\mu-\phi\mu\), donde \(\mu\) es la media y \(\varepsilon_{t}\) el ruido blanco con \(E(\varepsilon_{t})=0\). Así, una vez realizada la diferenciación y una serie temporal estacionaria \(w_{t}=(1-B)^{d}y_{t}\) es obtenida, los métodos autoregresivo de orden \(p\): AR\((p)\) y media movil de orden \(q\): MA\((q)\) pueden ser aplicados para tener un modelo completo.
Implementación del modelo ARIMA¶
Durante esta sección estudiaremos el uso de las APIs para cargar datos de interes de estudio, los cuales en esta sección corresponden a series de tiempo financieras, especificamente la serie de tiempo de las acciones de Apple cotizadas en bolsas de valores será considerada.
En las secciones pasadas los datos eran cargados a un
DataFrameusando la funciónread_csvde Python utlizando el link degithuben formatorawo simplemente con la dirección del directorio local donde reposaba el archivo en formato CSV que deseabamos cargar, en éste caso utlizaremos un protocolo de internet intermediario que nos permitirá obtener dichos datos.Las series de tiempo financieras son de gran interés personal, pero estos modelos pueden ser aplicados a cualquier tipo de proceso discreto que requiera de predicciones para la correcta toma de decisiones.
¿Qué es una API? API es el acrónimo de Application Programming Interface (Interfaz de Programación de Aplicaciones), que es un intermediario de software que permite que dos aplicaciones se comuniquen entre sí. Cada vez que usas una aplicación como Facebook, envías un mensaje instantáneo o consultas el tiempo en tu teléfono, estás usando una API. En su mayoría las API son privadas, algunas como la de
yahoo financeson libres hasta cierto límite, y para ciertofuturosyderivadosespecificos. Algunas entregan una versión de prueba, luego cuando la compañia que la ofrece nota un gran número de consultas (queries) por medio de su API a sus servidores desde una IP determinada, o de unBotpor ejemplo que hayas programado, comienzas a obtener mensajes de error direccionados a tu IP revocando la conexión. Otra opción interesante esQuandl, pero desafortunadamente ahora es adquisición deNasdaqy lo más seguro es que exisitrán ciertos costos para el uso de la API que no existian antes. Una API que esperamos se mantenga gratuita es la definnhub
Finnhub Stock API: En esta sección estaremos utlizando la API de finnhub. Finnhub proporciona una API de acciones en tiempo real gratuita para todos los operadores. También puede acceder a los datos de la empresa, el precio objetivo, las tendencias de recomendación, las ganancias y los datos de compensación de los directores generales a través de la API. Con la única misión de democratizar los datos financieros,
finnhubofrece una API gratuita en tiempo real para acciones, divisas y criptodivisas. Con esta API, podrá acceder a datos en tiempo real de más de 60 bolsas de valores, 10 corredores de divisas y más de 15 criptobolsas. También proporciona noticias en tiempo real para los mercados de empresas, acciones, divisas y criptomonedas. Los datos alternativos y de referencia de las empresas mundiales también se pueden recuperar a través de la API definnhub.
Para hacer uso de la API de
finnhub, debes crear una cuenta en su sitio web utilizando el correo de tu preferencia. Al crear tu cuenta automáticamente se generará una API Key que te permitirá ha cer uso de la API. Esta API Key es privada, la puedes volver a generar si la pierdes o si si está siendo usada por otra persona. La siguiente es una imágen del Dashboard definnhubdonde aparece tu API Key. Puedes hacer uso de Sandbox API Key para reducir el coste y el riesgo asociados a las llamadas a API de terceros durante los tests y permitir el desarrollo simultáneos para acelerar los ciclos de desarrollo de aplicaciones y reducir el time-to-market. Se utiliza para probar programas sospechosos que pueden contener virus u otro malware, sin permitir que el software dañe los dispositivos anfitriones. También puede usar el Webhook definnhub, como método de alteración del funcionamiento de una página o aplicación web, con callbacks personalizados. Es esta sección utlizaremos sólo la API Key, el resto de funcionalidades las podrá experimentar en los informes o proyectos finales. Para mas información acerca de la API definnhubvisitar la documentación correspondiente en su sitio web: https://finnhub.io/docs/api

Para usar la API de
finnhubdesde su notebook, deberá primero instalar usando el gestor de instalación pip la librería de Python requerida para hacer uso de la API usando la orden:pip install finnhub-python. Luego de hacer esto puede comenzar a usar las diferentes funciones públicas asociadas a la clasefinnhubque importamos desde Python luego de la isntalación. También importaremos librería para conversiones deunix timeadatetimeque realizará conversiones a un formato de fecha conocido, a saber: año, mes, día%Y-%m-%d. Las libreríasseabornymatplotlibya usadas en anteriores secciones también son importadas para realizar los gráficos correspondientes
import finnhub
import pandas as pd
from time import mktime
from datetime import datetime, timedelta
import time
import seaborn as sns
import matplotlib.pyplot as plt
La librería
warningses utlizada para evitar múltiples mensaje molestos de alertas no fatales, que en gran parte de los casos están relacionados con diferentes versiones de la librería en uso.
import warnings
warnings.filterwarnings("ignore")
Usamos las funciones
set_theme()para configurar el tema a usar en las figuras, en éste casopaperel cual invocamos usandoset_context("paper"). Para ver más temas para figuras conseabornvisitar: http://seaborn.pydata.org/tutorial/aesthetics.html
sns.set_theme()
sns.set_context("paper")
En la siguiente línea hacemos usa de la función
Clientde la clasefinnhubque se encarga de realizar la conexión con el clientefinnhub, utlizando nuesta API Key y crea un objeto instanciado que podemos usar para hacer uso de toda la funcionalidad ofrecida por la API. Debe escribir la API correctamente y entre comillas para que se realice correctamente la conexión. Dependiendo de su conexión a internet y su computadora, puede obtener mensajes de delay por parte del cliente, en estos caso simplemente vuelva a ejecutar la conexión con su API
finnhub_client = finnhub.Client(api_key="c5g56siad3id0d5no4vg")
Seleccione el stock de interés. Para éste ejemplo consideramos el precio de las acciones de AAPL con un timeframe diario. La función
datetime.now()nos entrega la fecha y hora actual, la funcióntimedelta()nos entrega en tiempo cero, 365 días del año que vamos a restar de la fecha actual. Esto es nuestra serie de tiempo corresponde a los últimos 365 precios de las acciones de Apple
stock = 'AAPL'
resolution = 'D'
end_date = datetime.now()
start_date = end_date - timedelta(days=365)
Para obtener información de nuestra vela de precios (candlestick) procedemos a hacer uso de la función
stock_candlesde la clasefinnhub_clientpara poder obtener información en la vela. Normalemente esta información corresponde al precio al que la acción en en día indicado, abrió, cerró, su valor máximo y mínimo, volumen tradeado y el tiempo dado en días en éste caso. Para aplicaciones avanzadas como las de HFT, que necesitan los precios a una frecuencia más alta cambiaríamos la resoluciónresolution = 'D'a la resolución que necesite, puede realizar hasta 60 llamados por minuto. La función.timestampnos permitirá obtener nuestra fecha en formatounix time, único formato que aceptará la funciónstock_candles. Imprima alguno de estos códigosunix timey utilice la siguiente calculadora https://www.epochconverter.com/ para verificar por ejemplo el código de la fecha y hora actual. Observe que la columnatdel DataFrame viene en formatounix time. Además nótese que se realizó un cambio de formato aDataFrame, esto es porque el formato original que viene definnhubes tipo JSON. Para facilitar el desarrollo de esta sección transformamos éste objeto JSON a unpandasDataFrame. Cada key del archivo JSON corresponde a los combres de cada columna y el value, es una lista con los 365 datos correspondientes a la columna
end = int(end_date.timestamp())
start = int(start_date.timestamp())
AAPL_res = finnhub_client.stock_candles(stock, resolution, start, end)
AAPL_df = pd.DataFrame(AAPL_res)
AAPL_df.head()
| c | h | l | o | s | t | v | |
|---|---|---|---|---|---|---|---|
| 0 | 116.32 | 121.9900 | 116.05 | 120.50 | ok | 1604880000 | 154515315 |
| 1 | 115.97 | 117.5900 | 114.13 | 115.55 | ok | 1604966400 | 138023390 |
| 2 | 119.49 | 119.6300 | 116.44 | 117.19 | ok | 1605052800 | 112294954 |
| 3 | 119.21 | 120.5300 | 118.57 | 119.62 | ok | 1605139200 | 103350674 |
| 4 | 119.26 | 119.6717 | 117.87 | 119.44 | ok | 1605225600 | 81688586 |
La siguiente función la utlilizaremos para convertir de formato
unix timeadatetime, la columnatde la fecha en nuestrDataFrameAAPL_df. Para aplicar la función a cada valor de la columnatusamos la funciónapply, estudiada anteriormente. Presentamos un ejemplo para que podamos visualizar el funcionamiento de la funcióndate_formatque recibirá como argumento un códigounix timey lo transformará adatetimeusando la funcióndatetime.fromtimestamp()
def date_format(date_col):
return datetime.fromtimestamp(date_col).strftime('%Y-%m-%d')
date_format(1602460800)
'2020-10-11'
AAPL_df['t'] = AAPL_df['t'].apply(date_format)
AAPL_df.head()
| c | h | l | o | s | t | v | |
|---|---|---|---|---|---|---|---|
| 0 | 116.32 | 121.9900 | 116.05 | 120.50 | ok | 2020-11-08 | 154515315 |
| 1 | 115.97 | 117.5900 | 114.13 | 115.55 | ok | 2020-11-09 | 138023390 |
| 2 | 119.49 | 119.6300 | 116.44 | 117.19 | ok | 2020-11-10 | 112294954 |
| 3 | 119.21 | 120.5300 | 118.57 | 119.62 | ok | 2020-11-11 | 103350674 |
| 4 | 119.26 | 119.6717 | 117.87 | 119.44 | ok | 2020-11-12 | 81688586 |
Luego cambiamos los nombres de cada columna (iniciales) al nombre original que se utliza en los candlesticks de series de tiempo financieras, como se muestra en la siguiente figura. Eliminamos la columna
sque no proporciona información relevante para las predicciones. Utilizaremos la funciónrenameydropya usadas anteriormente con éste objetivo. Consideraremos para éste ejemplo el precio de cierre de la acción, debido a que es el más utlizado cuando deseamos tomar decisiones de inversión

La siguiente figura es extraida de tradingview, y representa el precio diario de las acciones de APPLE cotizadas en Nasdaq uno de los principales exchange de futuros y derivados in the Street. Distintas predicciones se pueden realizar por medio del uso de análisis técnico y análisis fundamental por ejemplo. En éste caso usaremos el modelo ARIMA como herramienta predictiva para horizontes cortos. Si desea experimentar un poco el análisis técnico realizado por aproximadamente un 90% de los traders, recomiendo usar Bitmex Testnet e iniciar con una wallet con Bitcoins de prueba (paper version), para adquirir destrezas antes de realizar alguna inversión, el análisis técnico es una técnica que requiere de muchos años de experiencia y disciplina para obtener resultados exitosos.

AAPL_df.rename(columns = {'c':'Close','h':'High','l':'Low','o':'Open','t':'Date','v':'Volume'}, inplace = True)
AAPL_df.drop('s', axis=1, inplace = True)
AAPL_df.head()
| Close | High | Low | Open | Date | Volume | |
|---|---|---|---|---|---|---|
| 0 | 116.32 | 121.9900 | 116.05 | 120.50 | 2020-11-08 | 154515315 |
| 1 | 115.97 | 117.5900 | 114.13 | 115.55 | 2020-11-09 | 138023390 |
| 2 | 119.49 | 119.6300 | 116.44 | 117.19 | 2020-11-10 | 112294954 |
| 3 | 119.21 | 120.5300 | 118.57 | 119.62 | 2020-11-11 | 103350674 |
| 4 | 119.26 | 119.6717 | 117.87 | 119.44 | 2020-11-12 | 81688586 |
Utilizaremos la función
lineplotdeseabornpara realizar un gráfico de la serie de tiempo de interés. Dado que el volumen de datos es amplio, para poder visualizar fechas en el eje \(x\) por ejemplo usamos la funciónget_xticks()para obtener estas fechas cambiar la frecuencia a ser colocada solo en la figura, para poder tener una visualización agradable a la vista. Dado que los nombres de cada etiqueta corresponden a strings de fechas, para evitar que queden uno sobre otro, rotamos los labels en éste eje usano la funciónplt.xticks(rotation=50), nótese que se ha colocado;al final de éste llamado, ¿Con que objetivo?
plt.figure(figsize = (12,8))
ax = sns.lineplot(AAPL_df.Date, AAPL_df.Close)
freq = int(5)
xtix = ax.get_xticks()
ax.set_xticks(xtix[::freq])
plt.tight_layout()
plt.xticks(rotation=50);
Gráficos de velas en Python: El gráfico de velas es un estilo de gráfico financiero que describe la apertura, el máximo, el mínimo y el cierre para una coordenada \(x\) determinada (probablemente la hora). Los recuadros representan la dispersión entre los valores de apertura y cierre y las líneas representan la dispersión entre los valores bajos y altos. Los puntos de muestra en los que el valor de cierre es mayor (menor) que el de apertura se denominan crecientes (decrecientes). Por defecto, las velas crecientes se dibujan en verde mientras que las decrecientes se dibujan en rojo. Para realizar la figura usamos la función
Figure()de la clasegodeplotly, esta función recibe como input los datos asociados al candlestick suministrados por medio de la funciónCandlesticktambién de la clasegodeplotly
import plotly.graph_objects as go
fig = go.Figure(data=[go.Candlestick(x = AAPL_df.Date,
open = AAPL_df.Open,
high = AAPL_df.High,
low = AAPL_df.Low,
close = AAPL_df.Close)
])
fig.update_layout(
title="Apple Inc. (AAPL)",
xaxis_title="Day",
yaxis_title="AAPL-USD",
font=dict(
family="Courier New, monospace",
size=12,
color="RebeccaPurple"
)
)
fig.update_layout(xaxis_rangeslider_visible=False)
fig.show()
Utilizaremos ahora una prueba estadística para verificar si la serie de tiempo es estacionaria o no. Esta prueba es la de
Dickey-Fuller. En cursos avanzados de series de tiempo se estudia estadísticamente éste tipo de pruebas así como los plots ACF y PACF, en esta sección sólo mencionaremos en una bastante resumida la idea detras de su uso. Para hacer uso de esta prueba importamos la funciónadfullerde la clasestatsmodels.tsa.stattools. Para aplicar el test pasamos los datos del precio de cierre, serie de tiempo de interés, usandoAAPL_df.Close. Éste será el argumento de la funciónadfullerencargada de realizar el test deDickey-Fuller
from statsmodels.tsa.stattools import adfuller
from numpy import log
result = adfuller(AAPL_df.Close)
print('ADF Statistic: %f' % result[0])
print('p-value: %f' % result[1])
ADF Statistic: -1.615409
p-value: 0.475137
Dado que el valor crítico, el estadístico
ADF =-1,8 > -2,5,-3,4,-2,8(valores \(t\) en intervalos de confianza de 1%,5% y 10%), se acepta la hipótesis nula \((H_{o})\)Valor \(p-\)
= 0,480737 > 0,05. Dado que el valor \(p\) de Adfuller es mayor que un determinado valor alfa especificado (5%, nivel de significación o intervalo de confianza del 95%), se acepta la hipótesis nula \((H_{o})\), es decir, la serie temporal no es estacionaria
Realizaremos ahora figuras de autocorrelación para confirmar que la serie de tiempo es estacionaria, así como también para verificar cual es el orden de integración necesario para llevar nuestra serie de tiempo no estacionaria a una estacionaría. Para esto usaremos la función
plot_acfde Python, la cual recibe como argumentos, la serie de tiempo de interés y el número de lags* que deseamos considerar en la figura. Nótese que utlizamos la función.diff()para realizar diferenciación en nuestra serie de tiempo, con el objetivo de remover tendencia, además nótese también que se eliminan valoresnanque podamos obtener en éste proceso debido a que está figura consiste en graficar todos los valores \(\rho_{k}\) definidos anteriormente, donde aparece un cociente para el cual evitamos valores nulos en el denominador. Realizaremos tres figuras, correspondientes a la autocorrelación de la serie original, la serie diferenciada una vez, y dos veces. Colocaremos estas figuras en una matriz de \(3\times2\) usando la funciónfig, axes = plt.subplots(3, 2, sharex=True)dematplotlib. Para que cree que se usa el llamadosharex=True
import numpy as np, pandas as pd
from statsmodels.graphics.tsaplots import plot_acf
import matplotlib.pyplot as plt
plt.rcParams.update({'figure.figsize': (15,15)})
fig, axes = plt.subplots(3, 2, sharex=True)
axes[0, 0].plot(AAPL_df.Close); axes[0, 0].set_title('Original Series')
plot_acf(AAPL_df.Close, ax=axes[0, 1], lags = 240);
axes[1, 0].plot(AAPL_df.Close.diff()); axes[1, 0].set_title('1st Order Differencing')
plot_acf(AAPL_df.Close.diff().dropna(), ax=axes[1, 1], lags = 240);
axes[2, 0].plot(AAPL_df.Close.diff().diff()); axes[2, 0].set_title('2nd Order Differencing')
plot_acf(AAPL_df.Close.diff().diff().dropna(), ax=axes[2, 1], lags = 240);
Nótese el decaimiento geométrico en la primera figura de autocorrelación, que baja desde la parte positiva con una tendencia lineal, la cual es interpretada como una autocorrelación asociada a una serie de tiempo no estacionaría como indica la prueba de
Dickey-Fullerque presenta efectivamente tendencias. Los intervalos de confianza se dibujan como un cono. Por defecto, se establece un intervalo de confianza del 95%. Al observar el gráfico de autocorrelación para la segunda diferenciación, el retardo entra en la zona negativa con bastante rapidez, lo que indica que la serie podría haber sido sobrediferenciada.
Definimos ahora nuestro conjuntos de entrenamiento y testeo para el modelo ARIMA, en éste caso nuestro test corresponde a los últimos 7 precios de las acciones de AAPL, y el train corresponde a la serie de tiempo original menos estos 7 días. El mejor modelo será obtenido mediante uso de los datos de entrenamiento y el criterio de Akaike y de inferencia Bayesiana el cual penaliza la suma de los residuos al cuadrado por incluir parámetros adicionales en el modelo, los modelos que tienen valores pequeños del AIC o BIC se consideran buenos modelos. El primer término del AIC o BIC es la función de verosimilitud para el modelo ajustado evaluada en las estimaciones de máxima verosimilitud de los parámetros desconocidos. En este contexto, AIC y SIC se denominan criterios de verosimilitud penalizados. En esta sección seleccionaremos el modelo con el menor AIC debido a que el AIC es asintóticamente eficiente (menor error de predicción en nuevos datos) pero el BIC no lo es. Más sobre esta técnica y el principio de máxima verosimilitud para selecciona de un buen modelo ARIMA, es abordado en cursos electivos de análisis de series de tiempo
n_AAPL = len(AAPL_df.Close); n_test = 7 # This can be changed
train_size = n_AAPL - n_test
train = AAPL_df.Close.iloc[:train_size]
test_1w = AAPL_df.Close.iloc[train_size:train_size + n_test]
dates_1w = AAPL_df.Date.iloc[train_size:train_size + n_test]
print("train:", train.shape)
print("test_1w:", test_1w.shape)
train: (245,)
test_1w: (7,)
Utlizamos ahora el modelo
ARIMAimportado desde la libreríastatsmodelsde Python para obetener distintos ARIMA de ordenes \(p,d,q\). Consideramosmethod = 'mle'para el cálculo de la verosimilitud exacta a través del filtro de Kalman. Como ejercicio puede reescribir estas líneas de código en una función que dependa sólo del inputtrainy retorne los ordenes \(p, d, q\) asociados al criterio AIC de bondad de ajuste
from statsmodels.tsa.arima_model import ARIMA
import warnings
warnings.filterwarnings("ignore")
best_aic = np.inf
best_bic = np.inf
best_order = None
best_mdl = None
pq_rng = range(5)
d_rng = range(3)
for i in pq_rng:
for d in d_rng:
for j in pq_rng:
try:
tmp_mdl = ARIMA(train, order=(i,d,j)).fit(method='mle', trend='nc')
tmp_aic = tmp_mdl.aic
if tmp_aic < best_aic:
best_aic = tmp_aic
best_order = (i, d, j)
best_mdl = tmp_mdl
except: continue
print('aic: {:6.5f} | order: {}'.format(best_aic, best_order))
aic: 1053.04714 | order: (3, 1, 3)
Existe una función llamada
auto_arimade Python la cual es útli en ciertos casos especificos. Éste no es uno de ellos dado que el modelo ARIMA que entrega es un simple random walk \(x_{t}=x_{t-1}+\omega_{t}\), el cual predice puramente como un modelo estocástico con dependencia temporal basado totalmente en el punto temporal anterior \(t-1\)
import pmdarima as pm
model = pm.auto_arima(train, start_p=1, start_q=1,
test='adf', # use adftest to find optimal 'd'
max_p=3, max_q=3, # maximum p and q
m=1, # frequency of series
d=None, # let model determine 'd'
seasonal=False, # No Seasonality
start_P=0,
D=0,
trace=True,
error_action='ignore',
suppress_warnings=True,
stepwise=True)
Performing stepwise search to minimize aic
ARIMA(1,1,1)(0,0,0)[0] intercept : AIC=1055.416, Time=0.20 sec
ARIMA(0,1,0)(0,0,0)[0] intercept : AIC=1052.055, Time=0.01 sec
ARIMA(1,1,0)(0,0,0)[0] intercept : AIC=1053.830, Time=0.03 sec
ARIMA(0,1,1)(0,0,0)[0] intercept : AIC=1053.822, Time=0.03 sec
ARIMA(0,1,0)(0,0,0)[0] : AIC=1051.306, Time=0.01 sec
Best model: ARIMA(0,1,0)(0,0,0)[0]
Total fit time: 0.304 seconds
Por lo tanto para éste problema consideramos los mejores ordenes \(p, d, q\) obtenidos a a partir del criterio de Akaike. Los usamos como argumento de entrada en nuestro modelo ARIMA junto a nuestro train set, para obtener el modelo de ajustado de interés que utlizaremos para predecir valores futuros usando rolling
model = ARIMA(train, order=best_order)
model_fit = model.fit(method='mle', trend='nc')
Para graficar el ajuste de nuestro modelo ARIMA frente a nuestra serie de tiempo real utlizamos la función
.plot_predictque proviene del objeto instanciado usando la función ARIMA
plt.figure(figsize = (12,8))
model_fit.plot_predict(dynamic=False)
plt.title("Real vs. ARIMA Forecast (Train)", fontsize=12)
Text(0.5, 1.0, 'Real vs. ARIMA Forecast (Train)')
<Figure size 864x576 with 0 Axes>
Para medir el error de predicción cometido en las predicciones, utilizaremos las métricas usuales en análisis de series de tiempo:
MAPE, MAE, RMSE, MSE
def forecast_accuracy(forecast, actual, str_name):
mape = np.mean(np.abs(forecast - actual)/np.abs(actual)) # MAPE
mae = np.mean(np.abs(forecast - actual)) # MAE
rmse = np.mean((forecast - actual)**2)**.5 # RMSE
mse = np.mean((forecast - actual)**2) # MSE
df_acc = pd.DataFrame({'MAE': [mae],
'MSE': [mse],
'MAPE': [mape],
'RMSE': [rmse]},
index=[str_name])
return df_acc
Realizamos ahora predicciones utlilizando rolling forecasting. El rolling forecast entrega reportes que utilizan datos históricos para predecir cifras futuras de forma continua durante un periodo de tiempo. Las previsiones continuas se utilizan a menudo en los reportes financieros, la gestión de la cadena de suministro, la planificación y la elaboración de presupuestos. El rolling forecast es una ayuda esencial para tomar decisiones empresariales acertadas. Gracias a su capacidad de respuesta, las previsiones continuas ayudan a las empresas a responder más rápidamente a las condiciones cambiantes del mercado. Si se utilizan con eficacia, las previsiones continuas pueden ayudar a identificar las deficiencias de rendimiento, acortar los ciclos de planificación y tomar la mejor decisión para los resultados. Para nuestros ejemplos, nuestro horizonte de predicción será de un día
def arima_rolling(history, test):
predictions = list()
for t in range(len(test)):
model = ARIMA(history, order=best_order)
model_fit = model.fit(method='mle', trend='nc')
output = model_fit.forecast()
yhat = output[0][0]
predictions.append(yhat)
obs = test[t]
history.append(obs)
print('predicted=%f, expected=%f' % (yhat, obs))
return predictions
test_1wl = test_1w.tolist()
yhat_1w = arima_rolling(train.tolist(), test_1wl)
predicted=152.662026, expected=149.800000
predicted=149.785056, expected=148.960000
predicted=148.636426, expected=150.020000
predicted=149.622634, expected=151.490000
predicted=151.060422, expected=150.960000
predicted=150.917737, expected=151.280000
predicted=151.328948, expected=151.200000
Utilizaremos ahora las funciónes creadas anteriormente para calcular el error de predicción y
linpeplotpara graficarle junto a la serie de tiempo orginal de test
forecast_accuracy(np.array(test_1wl), np.array(yhat_1w), "week 1")
| MAE | MSE | MAPE | RMSE | |
|---|---|---|---|---|
| week 1 | 1.075665 | 2.061598 | 0.007137 | 1.435827 |
plt.figure(figsize = (8, 6))
sns.lineplot(dates_1w, yhat_1w, label="Forecast")
sns.lineplot(dates_1w, test_1wl, label="Test")
plt.tight_layout()
Tarea 2.2¶
Considere la serie de tiempo asociada con los futuros de la criptomoneda Bitcoin desde que comenzó a tradearse hasta la fecha del día de hoy. Utilice la API de
finnhubpara obtener esta serie de tiempo. Recuerde que para esto debe crear primero una cuenta, y así obtener su API Key para realizar la conexión de forma efectiva.Repita TODOS los pasos indicados en esta sección para encontrar modelos ARIMA para predecir el precio de Bitcoin con los siguientes horizontes: 7 días, 14 días, 21 días, 28 días. Utilizar siempre predicciones usando rolling con ventana de predicción continua de un día. Cualquier cantidad de pasos extra para enriqucer su anális predictivo serán aceptados siempre y cuando sean acordes con lo que indica la teoría de análisis de series de tiempo.
Realice una tabla de error utlizando las métricas:
MAPE, MAE, RMSE, MSE.Repita el análisis desarrollado en los pasos anteriores considerando ahora el criterio de inferencia Bayesiana (BIC) para encontrar el mejor modelo ARIMA y compare los errores con aquellos obtenidos con el criterio de Akaike.
Escriba en cada paso las concluiones y análisis estadísticos asociados con los resultados obtenidos. Realice un test de normalidad para los residuales obtenidos para cada predicción, en cada caso agregue las correspondientes conlusiones estadísticas. Figuras y algoritmos que no estén acompañados de una conclusión, descripción y análisis estadístico, no serán tenidas en cuenta.
Errores comunes al crear visualizaciones interactivas¶
Objetivos
Identificar los errores que se cometen al crear visualizaciones
Aplicar técnicas para corregir los errores y crear visualizaciones eficaces
Seleccionar y diseñar las visualizaciones adecuadas para determinados tipos de datos
Describir las diferentes bibliotecas y herramientas disponibles para crear visualizaciones
Introducción¶
En éste capítulo se enumera y explica los posibles fallos y errores que se cometen durante varias etapas del proceso de visualización de datos, como la visualización de elementos no correlacionados de un conjunto de datos para mostrar una relación o crear una característica interactiva inapropiada
El proceso de visualización de datos puede parecer sencillo: tome algunos datos, trace algunos gráficos, añadir algunas características interactivas, y el trabajo está hecho. O, tal vez, no lo sea: podrían haber varios lugares durante el trayecto en los que se pueden cometer errores. Estos errores terminan por dar lugar a una visualización defectuosa que no puede de manera fácil y eficaz transmitir lo que dicen los datos, lo que hace que sea completamente inútil para el público que lo está viendo.
Formato e interpretación de datos¶
La primera fase de la visualización de datos es entender los datos frente a ti: entender lo que es, lo que significa y lo que transmite. Sólo cuando entiendas los datos serás capaz de diseñar una visualización que ayude a otros a entenderlos.
Además, es importante asegurarse de que los datos tienen sentido y contienen suficiente información, ya sea categórica, numérica o una mezcla de ambas, para ser visualizados. Por lo tanto, si tratas con datos erróneos o sucios, es probable que la visualización sea defectuosa.
En esta siguiente sección, veremos algunas formas de evitar los errores comunes que se suelen cometidos en esta fase de los datos y cómo evitarlos.
¿Cómo evitar los errores más comunes al tratar con datos sucios?
Garbage In, Garbage Out - este es un dicho popular en el campo de la ciencia de datos, especialmente con respecto a la visualización de datos. Básicamente significa que si utilizas datos desordenados y ruidosos, vas a obtener una visualización defectuosa y poco informativa. Los datos desordenados, ruidosos y sucios corresponden a una serie de problemas que se encuentran en los datos. Vamos a discutir los problemas uno por uno y las formas de tratar este tipo de datos.
Valores atípicos
Los datos que contienen valores inexactos o instancias que son significativamente diferentes del resto de los datos de un conjunto de datos se denominan valores atípicos. Estos valores atípicos pueden ser auténticos, es decir, parecen incorrectos pero en realidad no lo son, o son errores que se cometen al recoger o almacenar los datos. Veamos un ejemplo de un error cometido al recoger o almacenar los datos. La siguiente tabla muestra la edad, el peso y el sexo de los clientes que acuden a un determinado gimnasio. La columna de sexo consta de tres valores discretos 0, 1 y 2 que corresponden todos a una clase: hombre, mujer y otro, respectivamente. La columna de la edad se expresa en años y la columna de peso está en kilogramos

Todo parece correcto hasta que llegamos a la cuarta instancia (índice 3), donde el peso peso es de 790 kg. Esto parece extraño porque nadie puede pesar realmente 790 kg, especialmente alguien cuya estatura es de 1,5 metros y 7 pulgadas. Quien haya almacenado estos datos debe haber querido decir 79 kg y haber añadido un 0 por error. Este es un caso de un valor atípico en el conjunto de datos. Esto puede parecer trivial en este momento, sin embargo, esto puede resultar en visualizaciones defectuosas, y predicciones o patrones de modelos de aprendizaje automático, especialmente si hay múltiples repeticiones de esos datos. Ahora, veamos un ejemplo de un auténtico valor atípico en la siguiente tabla

El peso en el cuarto caso (índice 3) es de 167 kilogramos, lo que parece extrañamente alto. Sin embargo, sigue siendo un valor verosímil, ya que es posible que alguien tenga un peso de 167 kilogramos a los 37 años. Por lo tanto, se trata de un un verdadero valor atípico. Mientras que en los ejemplos anteriores es fácil detectar el valor atípico, ya que sólo hay 5 casos, en realidad, nuestros conjuntos de datos son masivos, por lo que comprobar cada caso es una tarea tediosa y poco práctica. Por lo tanto, en la vida real, podemos utilizar visualizaciones estáticas básicas, como los gráficos de caja, para observar la existencia de valores atípicos. Los gráficos de caja son visualizaciones de datos sencillas pero informativas que pueden decirnos mucho sobre la sobre la forma en que se distribuyen nuestros datos. Muestran el rango de nuestros datos basándose en cinco valores clave:
El valor mínimo de la columna
El primer cuartil
La mediana
El tercer cuartil
El valor máximo de la columna
Esto es lo que hace que sean excelentes para mostrar los valores atípicos, además de describir la simetría de los datos, el grado de agrupación de los mismos (si todos los valores están repartidos en un amplio rango), y si están o no sesgados.
Ejercicio 52: Visualización de valores atípicos en un conjunto de datos con un gráfico de cajas¶
En este ejercicio, vamos a crear un gráfico de caja para comprobar si nuestro conjunto de datos contiene valores atípicos. Vamos a utilizar el conjunto de datos gym.csv, que contiene información sobre los clientes de un determinado gimnasio.
Importar las librerías necesarias
import pandas as pd
import numpy as np
import plotly.express as px
Guarda el archivo gym.csv en un DataFrame llamado gym, e imprime las cinco primeras filas del mismo para ver cómo son los datos
gym = pd.read_csv('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/gym.csv')
gym.head()
| age | weight | sex | |
|---|---|---|---|
| 0 | 29 | 88 | 2 |
| 1 | 45 | 96 | 1 |
| 2 | 35 | 91 | 0 |
| 3 | 37 | 790 | 1 |
| 4 | 27 | 62 | 0 |
Como puede ver, nuestros datos tienen tres columnas: edad, peso y sexo. La columna sexo consta de tres valores discretos que corresponden a tres clases discretas - 0 es hombre, 1 es mujer y 2 es otro.
Cree un gráfico de caja con el eje \(x\) como columna de sexo y el eje \(y\) como peso
fig = px.box(gym, x = 'sex', y = 'weight', notched = False)
fig.show()
La escala del eje \(y\) es extrañamente grande, ya que todos los gráficos de caja están comprimidos en la octava parte inferior del gráfico, por lo que no representa una visualización clara de los datos. Esto se debe al valor atípico en la cuarta instancia de nuestro
DataFrame790 kgTodos los valores parecen estar bien, excepto ese valor atípico en la parte superior del gráfico con max=790. Ahora, veremos las formas de tratar los valores atípicos.
Cómo tratar los valores atípicos
Hay tres formas principales de tratar los valores atípicos:
Eliminación: Si sólo hay unas pocas instancias (filas) que poseen valores atípicos, entonces esas instancias pueden ser eliminadas completamente del conjunto de datos, dejando así un conjunto de datos con cero valores atípicos. También hay ocasiones en las que una determinada característica (columna) contiene un gran número de valores atípicos. En tal caso, esa característica concreta puede eliminarse del conjunto de datos pero sólo si esa característica es insignificante. Sin embargo, la eliminación de datos no siempre es la mejor idea.
Imputación: La imputación es una opción mejor que la eliminación, especialmente si hay muchos valores atípicos en el conjunto de datos. Esto puede hacerse de tres maneras:
La forma más común es imputar los valores atípicos con la media, la mediana o la moda de la columna. Sin embargo, en el caso de muchos valores atípicos, estos valores pueden no ser lo suficientemente bueno, ya que cada valor atípico será un problema, debido a que cada uno de estos valores será sustituido por el mismo valor (la media, la mediana o la moda)
El otro método para obtener mejores valores para los valores atípicos, especialmente en el caso de de series temporales, es la interpolación lineal, es decir, el uso de polinomios lineales para crear nuevos puntos de datos dentro de un rango definido de puntos conocidos para reemplazar valores atípicos.
Un modelo de regresión lineal también puede utilizarse para predecir un valor que falta si es numérico, y en el caso de que el valor que falta sea categórico, se puede utilizar un modelo de regresión logística.
Por ejemplo, supongamos que tiene un conjunto de datos del que necesita mostrar una relación entre la altura y el peso. La columna de la altura tiene varios valores perdidos pero, como es una característica significativa, no puede eliminarla, ni tampoco puede imputar la media de la columna ya que eso podría llevar a una relación falsa. El conjunto de datos puede dividirse en dos conjuntos de datos
El conjunto de datos de entrenamiento, que contiene instancias sin valores perdidos
El nuevo conjunto de datos, que contiene sólo los casos en los que faltan valores en la columna de altura.
A continuación, se puede utilizar un modelo de regresión lineal en el conjunto de datos de entrenamiento. El modelo aprenderá de estos datos y, cuando se le proporcione el nuevo conjunto de datos, podrá predecir los valores de la columna de altura. Ahora, los dos conjuntos de datos pueden fusionarse juntos y ser utilizados para crear visualizaciones ya que no hay valores perdidos.
Transformación: Es el proceso de transformación de los valores atípicos mediante la construcción de la columna de datos en la que se encuentra el valor atípico, por ejemplo, convirtiendo los valores en porcentajes y utilizando esa columna como característica en lugar de la columna original. En la siguiente sección, veremos un ejercicio para entender cómo podemos tratar los los valores atípicos.
Ejercicio 53: Cómo tratar los valores atípicos¶
Detección de valores atípicos
Si nuestro conjunto de datos es pequeño, podemos detectar el valor atípico simplemente mirando el conjunto de datos. Pero si tenemos un conjunto de datos enorme, ¿cómo podemos identificar los valores atípicos? Tenemos que utilizar técnicas de visualización y matemáticas. A continuación se presentan algunas de las técnicas de detección de valores atípicos
Boxplots
Puntuación \(Z\)
Rango intercuantil (IQR)
Detección de valores atípicos mediante las puntuaciones \(Z\)
Criterio: Cualquier dato cuya puntuación \(Z\) esté fuera de la tercera desviación estándar es un valor atípico

Implementación:
Recorrer todos los datos y calcular la puntuación \(Z\) mediante la fórmula \((X_{i}-\mu)/\sigma\). Definir un valor de umbral de 3 y marcar los puntos de datos cuyo valor absoluto de puntuación \(Z\) sea mayor que el umbral como valores atípicos.
import numpy as np
outliers = []
def detect_outliers_zscore(data):
thres = 3
mean = np.mean(data)
std = np.std(data)
for i in data:
z_score = (i - mean)/std
if (np.abs(z_score) > thres):
outliers.append(i)
return outliers
sample_outliers = detect_outliers_zscore(gym.weight)
print("Outliers from Z-scores method: ", sample_outliers)
Outliers from Z-scores method: [790]
Detección de valores atípicos mediante el rango intercuantil (IQR)
Criterio: Los datos que se sitúan 1.5 veces del IQR por encima de \(Q_{3}\) y por debajo de \(Q_{1}\) son valores atípicos.
Implementación
Ordenar el conjunto de datos de forma ascendente
Calcular los cuartiles 1 y 3 \((Q_{1}, Q_{3})\)
Calcular \(IQR=Q_{3}-Q_{1}\)
Calcular el límite inferior = \((Q_{1}-1.5*IQR)\), el límite superior = \((Q_{3}+1.5*IQR)\), recorrer los valores del conjunto de datos y comprobar los que están por debajo del límite inferior y por encima del límite superior y marcarlos como valores atípicos
outliers = []
def detect_outliers_iqr(data):
data = sorted(data)
q1 = np.percentile(data, 25)
q3 = np.percentile(data, 75)
IQR = q3-q1
lwr_bound = q1-(1.5*IQR)
upr_bound = q3+(1.5*IQR)
for i in data:
if (i<lwr_bound or i>upr_bound):
outliers.append(i)
return outliers
sample_outliers = detect_outliers_iqr(gym.weight)
print("Outliers from IQR method: ", sample_outliers)
Outliers from IQR method: [790]
Tratamiento de los valores atípicos
Eliminación
En este ejercicio, vamos a eliminar la instancia que contiene el valor atípico del conjunto de datos que utilizamos en el Ejercicio 52, y visualizamos el conjunto de datos de nuevo generando un gráfico de caja basado en el nuevo conjunto de datos.
Importar las librerías necesarias
import pandas as pd
import numpy as np
import plotly.express as px
Guarda el archivo gym.csv en un DataFrame llamado gym, e imprime las cinco primeras filas del mismo para ver cómo son los datos
gym = pd.read_csv('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/gym.csv')
gym.head()
| age | weight | sex | |
|---|---|---|---|
| 0 | 29 | 88 | 2 |
| 1 | 45 | 96 | 1 |
| 2 | 35 | 91 | 0 |
| 3 | 37 | 790 | 1 |
| 4 | 27 | 62 | 0 |
Modificar el DataFrame del gimnasio para que sólo esté formado por los casos en los que el peso sea inferior a 103 e imprimir las cinco primeras filas:
gym_del = gym[gym.weight < 104]
gym_del.head()
| age | weight | sex | |
|---|---|---|---|
| 0 | 29 | 88 | 2 |
| 1 | 45 | 96 | 1 |
| 2 | 35 | 91 | 0 |
| 4 | 27 | 62 | 0 |
| 5 | 58 | 55 | 0 |
Vamos a crear un boxplot para ver el aspecto de los datos
fig1 = px.box(gym_del, x = 'sex', y = 'weight', notched = True)
fig1.show()
Imputación por medio de la media, mediana o moda
Como el valor medio es sensible a valores atípicos, se aconseja sustituirlos por la mediana
median = np.median(gym.weight)
print("median: ", median)
for outlier in sample_outliers:
gym.weight = np.where(gym.weight == outlier, median, gym.weight)
median: 76.0
gym.head()
| age | weight | sex | |
|---|---|---|---|
| 0 | 29 | 88.0 | 2 |
| 1 | 45 | 96.0 | 1 |
| 2 | 35 | 91.0 | 0 |
| 3 | 37 | 76.0 | 1 |
| 4 | 27 | 62.0 | 0 |
fig1 = px.box(gym, x = 'sex', y = 'weight', notched = True)
fig1.show()
Ejercicio 54: Tratamiento de los valores faltantes¶
Datos faltantes
Los datos perdidos son, como su nombre indica, valores que están en blanco (NaN, -, 0 cuando no deberían ser 0, etc.). Al igual que los valores atípicos, los valores perdidos pueden ser problemáticos tanto en el caso de las visualizaciones como en el de los modelos predictivos.
Los valores faltantes en las visualizaciones pueden mostrar una tendencia que en realidad no existe o no representa una relación entre dos variables que, en realidad, es significativa. Aunque es posible crear visualizaciones con un conjunto de datos que contenga valores perdidos, no se recomienda hacerlo. Al hacerlo, se ignoran los casos en los que se encuentran esos valores perdidos, creando así una visualización basada en algunos de los datos pero no en todos.
Por lo tanto, el tratamiento de los valores perdidos es de suma importancia. Existen dos enfoques principales para tratar los valores perdidos: la supresión y la imputación, ambos discutidos en términos de tratamiento de los valores atípicos. La La misma lógica se aplica a los valores perdidos.
En este ejercicio, vamos a trabajar con un conjunto de datos que tiene siete valores perdidos en forma de 0s. En primer lugar, eliminaremos las instancias que contienen estos valores perdidos y generar un gráfico de caja para ver el impacto que la eliminación de un gran número de instancias en nuestra visualización. A continuación, imputaremos el valor de la mediana de la columna que que contiene los valores perdidos y generaremos un gráfico de caja basado en este conjunto de datos imputados.
Importar las librerías necesarias
import pandas as pd
import numpy as np
import plotly.express as px
Guarda el archivo weight.csv en un
DataFramellamado weight, e imprime las cinco primeras filas del mismo para ver cómo son los datos y utiliza la función.describe()para mostrar información sobre ella
w = pd.read_csv('https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/weight.csv')
w.head()
| weight | sex | |
|---|---|---|
| 0 | 47 | 2 |
| 1 | 0 | 1 |
| 2 | 35 | 0 |
| 3 | 34 | 1 |
| 4 | 0 | 0 |
w.describe()
| weight | sex | |
|---|---|---|
| count | 62.000000 | 62.000000 |
| mean | 33.887097 | 0.838710 |
| std | 15.683451 | 0.813685 |
| min | 0.000000 | 0.000000 |
| 25% | 25.000000 | 0.000000 |
| 50% | 35.000000 | 1.000000 |
| 75% | 46.000000 | 1.750000 |
| max | 56.000000 | 2.000000 |
Como podemos ver, el valor de peso mínimo en nuestro conjunto de datos es 0; sin embargo, nadie puede pesar 0 kgs, lo que significa que tenemos valores faltantes en forma de 0s. Intentemos eliminar estas instancias.
Cree un nuevo
DataFrameque conste sólo de las instancias en las que el peso no sea igual a 0. Muestre información sobre este nuevoDataFrame
doc_w = w[w.weight != 0]
doc_w.describe()
| weight | sex | |
|---|---|---|
| count | 55.00000 | 55.000000 |
| mean | 38.20000 | 0.836364 |
| std | 10.49056 | 0.811118 |
| min | 21.00000 | 0.000000 |
| 25% | 31.00000 | 0.000000 |
| 50% | 36.00000 | 1.000000 |
| 75% | 46.50000 | 1.500000 |
| max | 56.00000 | 2.000000 |
Crea un
boxplotcon este nuevoDataFrame, con el eje \(x\) como sexo y el eje \(y\) como peso
fig1 = px.box(doc_w, x = 'sex', y = 'weight', notched = True)
fig1.show()
Ahora, el valor del peso mínimo es 21, lo que tiene más sentido. Sin embargo, nuestro conteo se ha reducido a 55 de 62, lo que significa que hemos eliminado 7 instancias de nuestro conjunto de datos. Esto puede parecer pequeño en este ejemplo, pero en realidad, esto puede tener repercusiones serias en la información obtenida.
Además, en el gráfico de caja anterior, el extremo inferior de la caja para el sexo 0 y el extremo superior de la caja para el sexo 2 es ligeramente anormal. Por lo tanto, sustituyamos los valores 0 de la columna de peso por el valor medio de la columna. Recuerde que tenemos que calcular la media de la columna ¡columna sin tener en cuenta esos valores 0! Si los tenemos en cuenta, entonces nuestra media será incorrecta.
Calcular la media de la columna de pesos del
DataFrameque consiste en sólo valores de peso distintos de cero
mean_w = doc_w['weight'].mean()
mean_w
38.2
Utilice la función
.replace()para sustituir los valores 0 presentes en la columna weight delDataFrameoriginal por la media de la columna weight del modificado. Guarde esto en un nuevoDataFrame
w_new = w.replace({'weight': {0: mean_w}})
Muestra la información del nuevo
DataFrame
w_new.describe()
| weight | sex | |
|---|---|---|
| count | 62.000000 | 62.000000 |
| mean | 38.200000 | 0.838710 |
| std | 9.870307 | 0.813685 |
| min | 21.000000 | 0.000000 |
| 25% | 31.250000 | 0.000000 |
| 50% | 38.100000 | 1.000000 |
| 75% | 46.000000 | 1.750000 |
| max | 56.000000 | 2.000000 |
Nuestro recuento es de 62, lo que significa que tenemos todas las instancias, y nuestro peso mínimo es 21, lo que significa que no tenemos ningún 0.
Cree un gráfico de caja con este nuevo
DataFrame, con el eje \(x\) como sex y el eje \(y\) como weight.
fig2 = px.box(w_new, x = 'sex', y = 'weight', notched = True)
fig2.show()
Ahora, tenemos una visualización que no tiene valores perdidos y representa todas las instancias que están presentes en el conjunto de datos. Veamos el tercer problema que puede generar una visualización defectuosa.
Instancias y/o funciones duplicadas
El tercer problema es la presencia de instancias y/o características duplicadas en un conjunto de datos. Se trata de elementos innecesarios en el conjunto de datos y, si no se eliminan, pueden pueden afectar a las tendencias y a los conocimientos que se muestran en una visualización.
Por ejemplo, puede crear una visualización que muestre la relación entre el género de un adolescente y si toca el piano. Con un conjunto de datos sin valores atípicos, anomalías o valores perdidos, obtendrá una gran visualización. A partir de la visualización, podrá también podrá concluir que hay más mujeres que tocan el piano que hombres. Sin embargo, digamos que la visualización proviene del conjunto de datos siguiente

Hay dos instancias para Nita Thadaka y tres instancias para Pooja Rajesh, lo que significa que hay tres instancias duplicadas en total. Esto significa que la información que la visualización está proporcionando es inexacta. La forma de tratar los duplicados es sencilla: eliminarlos. Con este fin puede utlizar la función:
.drop_duplicates()
Mala selección de características
Con respecto a un conjunto de datos, una característica es una columna en el conjunto de datos, mientras que una instancia es una fila en el conjunto de datos. Por ejemplo, en la tabla anterior, nombre, sexo, tocar el piano y la edad son características, mientras que Pooja Rajesh, F, Yes y 17 es una instancia.
Dado que el objetivo de una visualización es mostrar una tendencia, un patrón, una relación o algún vínculo entre dos o más características en un conjunto de datos, es importante que la selección de esas características se haga con cuidado. Por lo tanto, este es un punto crucial en la visualización de datos de datos. Si el objetivo es transmitir que existe una fuerte relación entre dos características, entonces hay que Si el objetivo es transmitir que existe una fuerte relación entre dos características, hay que asegurarse de que están fuertemente correlacionadas antes de proceder a su visualización. La selección de características insignificantes dará lugar a una visualización sin sentido y no acabará no transmitirá ninguna información concreta. Por ejemplo, en cuanto al conjunto de datos co2.csv, el conjunto de datos contiene información sobre las emisiones de dióxido de carbono por país y el PIB por país. Comprobamos la correlación entre las emisiones de CO2 y el PIB antes de visualizar el conjunto de datos, garantizando que íbamos a crear una una visualización que valiera la pena.
Tarea 2.3¶
Determine las características que se visualizan en un gráfico de dispersión. Se le da el conjunto de datos co2.csv y se le pide que proporcione información sobre él, como por ejemplo, qué tipo de patrones existen, si hay tendencias entre las características, etc. Es necesario que su visualización final transmita información significativa. Para conseguirlo, va a crear visualizaciones para diferentes emparejamientos de características para entender están correlacionadas y, por lo tanto, vale la pena visualizarlas.
Pasos principales
Importe las bibliotecas necesarias.
Vuelva a crear el
DataFrame. Desde elDataFramegmincluya las columnaspopulation,fertility, ylifeVisualiza la relación entre el
co2ylifeutilizando un gráfico de dispersión, con el nombre del país como información en la herramienta hover y el año como deslizador.Comprueba la correlación entre
co2ylife.Visualiza la relación entre
co2yfertilitymediante un gráfico de dispersión, con el nombre del país como información en la herramienta hover y el año como deslizador.Comprueba la correlación entre
co2yfertility
Importación y cargue de datos en PostgreSQL usando Heroku¶
Objetivos
Crear un base de datos en Heroku Postgres y verificar conexión y e importación de tablas usando pgAdmin de PostgreSQL
Utilizar alternativas proporcionadas por el cliente de Heroku para solucionar el problema de las 20 conexiones
Utilizar la API de Python para PostgreSQL para importar datos desde un archivo CSV a Heroku Postgres y luego realizar consultas a nuestras tablas creadas y estudciar la forma de realizar cálculos especificos mediante consultas SQL
Generar visualizaciones a partir de las tablas creadas
Introducción¶
Herokues una empresa que se especializa en ofrecer servicios de plataforma administrada, por sus siglas en inglés PasS, en otras palabras ofrece servicios de servidores y redes administrados por Heroku en donde se pueden alojar aplicaciones de diferentes lenguajes de programación como Python, Java, PHP y más. Los servicios que Heroku ofrece están claramente orientados para desarrolladores, startups y empresas pequeñas. Heroku nació en el 2007 y fue adquirida en el 2010 por salesforce.com.Herokuutiliza contenedores Linux (Ubuntu) los cuales son llamados “dynos”, estos son utilizados para alojar las aplicaciones web, webservices o aplicaciones que se ejecutan del lado del servidor, así mismo cuenta con la posibilidad de instalar add-ons para agregar funcionalidades a dichos contenedores, por ejemplo, se pueden agregar servicios administrados de base de datos, almacenamiento en la nube o mensajería(MQ) por mencionar algunos.Herokutiene su clientela bien definida: empresas que quieren dejar de preocuparse por cuestiones de infraestructura y sólo enfocarse en el desarrollo. Por lo general estas suelen ser empresas grandes o startups que prefieren no invertir en un equipo de operaciones cuando están en una etapa temprana, y su prioridad debe ser hacer un producto que las personas quieran.Herokutiene dos tiers, o niveles, para personas interesadas en aprender: una versión gratuita similar a la de now.sh, que entra en modo “sleep” cada 30 minutos sin tráfico, y otra de 7 USD que compite con el servicio básico de 5 USD al mes que ofrece Digital Ocean, pero agregar las ventajas de que nuestros servidores sean administrados por nosotros.
PostgreSQLes un sistema para gestionar bases de datos de muy alto nivel, completamente de software libre y con una licencia BSD, compatible con cualquier uso, ya sea personal o comercial. Este sistema tiene como precursor otro sistema gestor de bases de datos, llamado INGRES, que fue uno de los primeros intentos de implementar un sistema de bases de datos relacional. INGRES abrió el camino para muchos otros sistemas conocidos como Sybase, Informix o el propio SQL Server. El impulsor de INGRES lideró posteriormente también el desarrollo de PostgreSQL, cuyo nombre del proyecto hace referencia a su propia raíz (Post-Ingres).PostgreSQLes un sistema considerado como empresarial. Tratándose de la aplicación de bases de datos más avanzado de código abierto, podría utilizarse por su funcionalidad y potencia como reemplazo de otras bases de datos comerciales, incluso del poderoso Oracle. Hasta cierto punto, resulta extraño que no sea el más popular, condición que ostenta MySQL.PostgreSQLtiene dos ventajas fundamentales, primero en lo que respecta a su funcionalidad y capacidad de trabajar con mayores cantidades de datos, pero también en lo que respecta a su licencia. MySQL tiene una licencia dual, lo que significa que para proyectos comerciales habría que pagar por su uso. Sin embargo, PostgreSQL tiene una única licencia totalmente abierta para cualquier uso. Algunas de sus principales características son: Alta concurrencia, Soporte para múltiples tipos de datos de manera nativa, Soporte a triggers, Trabajo con vistas, Objeto-relacional, Soporte para bases de datos distribuidas, Soporte para gran cantidad de lenguajes como C, C++, Java, PHP o Python. Además, ofrece interfaces para ODBC y JDBC, así como interfaces de programación para infinidad de lenguajes de programación.
Creación de base de datos Heroku Postgres¶
Antes de seguir con los siguientes pasos debe tener isntalado
PostgreSQL. El objetivo en esta sección es, luego de crear la base de datos en la nubeHeroku Postgres, realizaremos conexiones a esta misma utlizandopgAdmincon el objetivo de verificar que ésta fue creada exitosamente. Para instalarPostgreSQLpuede seguir cada uno de los siguientes tutoriales, seleccionados de los muchos que puede encontrar en la red. Si se siente a gusto con algún otro tutorial en el que realicen el mismo procedimiento, adelante. Sólo tenga en cuenta, agregar todas las componentes dePostgreSQLen el proceso de instalación
Una vez tiene isntalado
PostgreSQL, procedemos con la creación de la base de datos enHeroku.Heroku Postgresqlrequiere de la creación de unaappinical, para poder crear la base de datos. No es necesario aún desplegar su aplicaciónDashoShinyenHeroku, sólo usaremo la nube para crear nuestra base de datos y cargar nuestras tablas. Primero que todo acceda al sitio web de Heroku, haga click en la parte superior derecha Sign up y cree una cuenta

Creada la cuenta en
Herokuprocedemos a la creación de unaapppara poder crear la base de datos. Laappes la unidad fundamental de organización enHeroku. Cadaapppuede estar asociada a su propio conjunto de complementos provisionados. Hacemos click en Create new app en la pagina inicial en nuestra, al ingresar a nuesta cuenta enHeroku

Crea una
appcon el nombre de tu preferencia. La plataforma no acepta nombres con guión bajo, pero si al medio. Podrás crear otra luego de este primer test otraapp, sólo trata esta primera vez de seguir cuidadosamente cada paso.

Luego de realizar éste procedimiento visualizarás la
appdeHerokuen la siguiente forma. En éste lugar puedes desplegar también tu app deDashoShinyque estudiaremos en la siguiente sección. Esta vez, sólo utilizaremos laapppara crear una instancia para nuestra base de datos. Haga click en Resources con este objetivo

Luego le va a aparecer la siguiente ventana. Debe hacer click en Find more add-ons. Procedemos a instalar el
addonpara instalar la base de datos basada enPostgreSQL. Aparecerá una lista de variosaddonsque no usaremos en este momento, seleccionaremos de la lista aHeroku Postgres

Asegurese de que está instalando la versión gratuita de
Heroku Postgres. Debe aparecer el mensaje que vemos en pantalla Starting at 0/mo y más aabajo Hobby Dev Free. Nótese también que en esta página aparece la capacidad de almacenamiento, número máximo de filas que debe tener la tabla que importemos enPostgreSQLy además el número de conexiones, al cual buscaremos una solución alternativa, mas adelante. Haga click en Install Heroku Postgres y asegurese de estar logeado con tu cuenta

Luego de realizar la instalación, le aparecerá la siguiente pantalla en la que deberá seleccionar la
appque creamos anteriormente. Haga click en Submit Order Form para finalizar la creación de su base de datos y espere unos segundos

Exploremos ahora la base de datos que hemos creado. Nótese que el costo estimado mensual es 0. Efectivamente esto ocurre porque hemos seleccionado la versión gratuita Hobby Dev - Free. Hagamos click en Heroku Postgres para buscar la información de conexión a nuestra nueva base de datos

Por ahora no veremos ninguna conexión, hasta que comencemos a usar la la base de datos. Optaremos por usa pgAdmin a manera de test, con el objetivo principal de verificar que podemos realizar conexiones a nuestra base de datos
Heroku Postgres.

Haga click en Settings y View Credentials para poder acceder a la información necesaria para la conexión a la base de datos. Estos datos son los que usaremos mas adelante para conectarnos a esta base de datos con pgAdmin de
PostgreSQLy también desde nuestro notebook dePython, utlizando la API encargada de esto. La información en el recuadro es la más importante, pues es la que usaremos para realizar nuestra conexión a nuestra base de datos

pgAdmin PostgreSQL
Ahora realizaremos una prueba de conexión utilizando pgAdmin, la cual viene incluida en la instalación de
PostgreSQL. Inice la aplicación, le pedirá la contraseña que utlizó durante la instalación. Vamos a crear un servidor remoto el cual usaremos para conectarnos a la base de datos Heroku Postgres. Haga click derecho en Servers luego Create/Server como en la siguiente imágen

Coloque el nombre de su preferencia al servidor que usaremos para conectarnos a la base de datos. Usted puede crear una base de datos desde su computadora. Su computadora funcionaría como un servidor local, a diferencia de Heroku que es un servidor en la nube. Por otro lado también puede crear Dockers para desplegar aplicaciones. Un Docker es un sistema operativo (o runtime) para contenedores. El motor de Docker se instala en cada servidor en el que desee ejecutar contenedores y proporciona un conjunto sencillo de comandos que puede utilizar para crear, iniciar o detener contenedores. Heroku usa Dockers para el funcionamiento de sus servidores. Aunque Heroku facilita mucho la creación de base de datos y el despliegue de aplicaciones, con Docker obtienes infinitamente más flexibilidad y portabilidad porque el usuario controla el contenedor subyacente en lugar de tenerlo definido por Heroku. Cual usar va a depender de sus necesidades

Dirijase a la casilla Connection y coloque en Host name/address, el Host de Heroku Postgres. Mantenga el Port como 5432. Un puerto es esa interfaz a través de la cual se pueden enviar y recibir los diferentes tipos de datos. En la casilla Maintenance database colocaremos el nombre de nuestra base de datos en Heroku que aparece como Database. En la casilla Username colocaremos el nombre de usuario en Heroku que aparece como User. Luego colocamos el Pasword llamado de la misma forma en pgAdmin y Heroku Postgres. Debe verse la casilla Connection como se ve a continuación

Además de esto debe colocar el nombre de la base de datos creada en Heroku denotado como Database en la casilla Advanced. Esto lo hacemos para poder conectarnos a nuestra base de datos y no a una de las tantas gratuitas, creadas por otros usuarios en el servidor ofrecido por Heroku. Al final en pgAdmin haga click en Save

Luego de esto podrá ver que se ha creado el servidor en pgAdmin con el nombre indicado. Ahora estamos listos para probar conexión a la base de datos en Heroku utlizando éste nuevo servidor

Para verificar que se ha conectado a su base de datos en Heroku Postgres, haga click en overview y vera que el número de conexiones ha cambiado

Pasemos ahora a crear un tabla de prueba en la base de datos desde pgAdmin para confirmar conexión y además visualizar el cambio en el número de tablas desde Overview. Haga click desde pgAdmin en el servidor creado con su nombre de preferencia, en este ejemplo HerokuServer, luego haga click en Tools/Query Tool para realizar una consulta a nuestra base de datos en Heroku Postgres

Verifique que en la parte superior aparece la base de datos correcta a la que estamos conectados. Creamos luego una tabla llamada
accountsconuser_idyname, para verificar que todo funciona correctamente. Seleccione el código para crear la tabla y luego presionamos el botón de Run para ejecutar elQuery. Debe aparecer el mensaje de consulta exitosa automáticamente. Profundizaremos en la proxima sección acerca del significado de estas consultas

Haga click luego en Schemas/Tables y vera que fue creada la tabla
accountsen la base de datos Heroku Postgres. Nótese que en Heroku aparecerán 2 conexiones y la tabla que fue creada automáticamente en TABLE

Realicemos ahora un nuevo registro en la tabla creada, el cual vamos a poder evidenciar en overview con el ítem ROW. Utilizaremos las consultas qeu observa en la siguiente imágen. INSERT INTO es del lenguaje SQL y es utilizada para insertar datos en la tabla creada. SELECT * FROM es utlizada para seleccionar columnas de nuestra base de datos, en éste caso usamos
*para indicar que queremos seleccionar y a su vez visualizar los datos en la tabla. Nótese que ya aparece actualizado en Heroku Postgres el ítem ROW con el nuevo registro o fila agregada a la tabla

Cuando regrese al inicio, o ingrese nuevamente a Heroku aparecerá en la primera venta la
appque contiene la base de datos. Podrá aceder a ella las veces que lo desee para verificar el número de conexiones y que se hayan importado las tablas correctamente

Habrá notado que el número de conexiones a Heroku Postgres está límitado a 20 conexiones, esto suele ser un problema cuando contamos con aplicaciones que a menudo para su correcto funcionamiento realizan múltiples consultas a la base de datos. Con el fin de resolver éste problema usted puede construr un script que elimine el número de conexiones cada hora por ejemplo. La pregunta ahora sería, ¿Como eliminar estas 20 conexiones a Heroku Postgres?. Algunos desarrolladores con éste fin, utilizan heroku-pg-extras, esta librería puede ser utilizada para reiniciar el número de conexiones a Heroku Postgres. Esto va a requerir primero instalar el cliente de Heroku Heroku CLI. La instalación del cliente puede ser realizada en cualquier sistema operativo, en ésta sección a manera de ejemplo realizaremos la instalación en sistema opertativo
Windows, y además porque cuenta con el mayo número de usuarios, un total de 1.5 billones, en comparación con los 100 millones de usuarios que usanMacy 40 millones que usanUbuntu. El procedimiento para el resto de sistemas operativos es bastante similar, pues todo sera ejecutado desde la línea de comandos. El link de descarga del cliente Heroku CLI lo llevara a la página de instalación, donde debe escoger el binario asociado con su sistema operativo

Mantenga todos los check por default durante la instalación de Heroku CLI, y haga click en Install

Luego que el procedimiento de instalación ha finalizado, verificamos que la instalación fue realizada con éxito. Para esto en nuestro Command Prompt o Windows PowerShell, vamos a ejecutar la siguiente orde:
heroku version

Antes de usar el cliente debemos iniciarlo con nuestra cuenta de Heroku. Para esto utilizaremos el siguiente comando el cual nos permitirá inciar el cliente pasando antes por el inicio de sesión en nuestra cuenta de Heroku

Haga click en Log In para iniciar sesión desde el cliente de Herokude desión

En la ventana de comando podrá ver que ya ha iniciado sesión en el cliente de Heroku. Usando la orden
heroku appsen nuestro línea de comandos podremos visualizar lasappsque tenemos creadas, en este caso una sola, la que hemos usado en esta sección

Si aparece un mensaje de warning relacionado con la actualización del cliente, podemos hacer este procedimiento con la orden:
heroku update

Ahora si puede instalar el plugin que permite matar todas las conexiones. Este plugin se utiliza para obtener información sobre una instancia de Heroku Postgres, que puede ser útil al analizar los problemas de rendimiento. Esto incluye información sobre los bloqueos, el uso de los índices, los ratios de éxito de la caché del buffer y las estadísticas de vacío. Para su instalación utlice la orden:
heroku plugins:install heroku-pg-extras. Puede ignorar los mensajes de warning, pues estos no afectarán al funcionamiento de losheroku-pg-extras

Escriba
heroku help pgpara visualizar la forma de usarheroku-pg-extrasen el cliente de Heroku

Siguiendo las intrucciones de uso de
heroku-pg-extraspodemos visualizar información acerca de laappcreada en Heroku con la orden:heroku pg:info --app dataviz-lrubio-postgresql-test

Para matar todas las conexiones a Heroku y dejarlas en 0, para tener nuevamente las 20 posibles disponibles, debemos utlizar
pg:killall. En el caso que tenga unaappque realice muchas consultas, calcule cada cuanto completa por ejemplo las 20, por ejemplo, cada 5 minutos, y luego cree un script que se active automaticamente cada 5 minutos el cual se encarge de ejecutarpg:killall

Existen distintas opciones para evitar éste tipo de procedimiento. La primera de ellas es contratar el servicio mensual de Heroku. Algunos prefieren usar directamente AWS o Google Cloud, pero estos son pagos también, necesitarás usar tu tarjeta de crédito. Las versiones “gratuitas” de AWS te permiten crear bases de datos y desplegar apps, pero costos son generados siempre, incluso cuando apagas el servicio, automáticamente se activa cada 7 días generandote costos. Otra opción es usar por ejemplo Google Cloud, la cual es de agrado para muchos desarrolladores en la nube, aunque es también paga, te ofrece 3 meses gratuitos, los cuales puedes usar a tu gusto y luego decidir si mantienes o cierras tu cuenta. Por ejemplo para graficos con mapas usando
ggmap, en la sección deRusaremos este plan gratuito para acceder a mapas de Google. También esta la opción de usar Docker directamente.Dockeres un servicio de virtualización de aplicaciones dentro de unos denominados contenedores. Esto permite que las aplicaciones se ejecuten independientemente del host que los alberga. Si eres usuario deWindowsoMacnecesitarás tener instaladoDocker Desktopen tu pc o portátil y éste debe tener cierta capacidad y potencia. Por otro lado, en el caso de que seas usuario deWindows, deberás tener la versiónProfessionalpara poder instalarDocker. Si no es así, debe hacerlo desdeUbuntu.
Consultas desde Python a Heroku Postgres¶
PostgreSQLpuede ser integrado conPythonusando el módulopsycopg2.psycopg2es un adaptador de base de datosPostgreSQLpara el lenguaje de programaciónPython.psycopg2fue escrito con el objetivo de ser muy pequeño, rápido y estable como una roca. No necesitas instalar este módulo por separado porque se entrega, por defecto, junto con la versión2.5.xdePythonen adelante. Sin embargo, si no lo tienes instalado en tu máquina, puedes usar los siguientes comandos según tu preferencia de gestor de paquetespip install psycopg2conda install -c anaconda psycopg2
Para utilizar el módulo
psycopg2, primero debe crear un objetoconnectionque represente la base de datos y luego, opcionalmente, puede crear un objetocursorque le ayudará a ejecutar todas las sentenciasSQL.
APIs del módulo Python psycopg2
Las siguientes son rutinas importantes del módulo psycopg2, que pueden satisfacer su necesidad de trabajar con la base de datos PostgreSQL desde su programa Python. Si usted está buscando una aplicación más sofisticada, entonces usted puede mirar en la documentación oficial del módulo Python psycopg2.
Esta API abre una conexión a la base de datos PostgreSQL. Si la base de datos se abre con éxito, devuelve un objeto de conexión
psycopg2.connect(database="testdb", user="postgres", password="cohondob", host="127.0.0.1", port="5432")
Esta rutina crea un cursor que se utilizará durante toda la programación de la base de datos con
Python
connection.cursor()
Esta rutina ejecuta una sentencia
SQL. La sentenciaSQLpuede ser parametrizada (es decir, marcadores de posición en lugar de literalesSQL). El módulopsycopg2soporta marcadores de posición utilizando el signo%s. Por ejemplo:cursor.execute("insert into people values (%s, %s)", (who, age))
cursor.execute(sql [, optional parameters])
Esta rutina ejecuta un comando SQL contra todas las secuencias de parámetros o mapeos encontrados en la secuencia sql
cursor.executemany(sql, seq_of_parameters)
Este atributo de sólo lectura que devuelve el número total de filas de la base de datos que han sido modificadas, insertadas o eliminadas por la última ejecución
cursor.rowcount
Este método consigna la transacción actual. Si no se llama a este método, cualquier cosa que se haya hecho desde la última llamada a
commit()no es visible desde otras conexiones de la base de datos
connection.commit()
Este método revierte cualquier cambio en la base de datos desde la última llamada a
commit()
connection.rollback()
Este método cierra la conexión a la base de datos. Tenga en cuenta que esto no llama automáticamente a
commit(). Si simplemente cierra la conexión a la base de datos sin llamar primero acommit(), ¡los cambios se perderán!
connection.close()
Este método obtiene la siguiente fila de un conjunto de resultados de consulta, devolviendo una única secuencia, o None cuando no hay más datos disponibles
cursor.fetchone()
Esta rutina obtiene el siguiente conjunto de filas de un resultado de consulta, devolviendo una lista. Se devuelve una lista vacía cuando no hay más filas disponibles. El método intenta obtener tantas filas como indique el parámetro de tamaño.
cursor.fetchmany([size=cursor.arraysize])
Esta rutina recupera todas las filas (restantes) de un resultado de consulta, devolviendo una lista. Se devuelve una lista vacía cuando no hay filas disponibles.
cursor.fetchall()

Conexión a la base de datos
El siguiente código de
Pythonmuestra cómo conectarse a una base de datos existente. Para conectar la base de datosPostgreSQLy realizar consultasSQL, debes conocer el nombre de la base de datos a la que te quieres conectar. EN éste caso la información de la base de datos creada la encontramos en Heroku Postgres. Importamos primero las librerías necesarias para usarpsycopg2
import psycopg2
from psycopg2 import Error
try:
connection = psycopg2.connect(user="xoihbshcjgwwtd",
password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
host="ec2-34-205-14-168.compute-1.amazonaws.com",
port="5432",
database="d4oenn2g04nlqm")
cursor = connection.cursor()
print("PostgreSQL server information")
print(connection.get_dsn_parameters(), "\n")
cursor.execute("SELECT version();")
record = cursor.fetchone()
print("You are connected to - ", record, "\n")
except (Exception, Error) as error:
print("Error while connecting to PostgreSQL", error)
finally:
if (connection):
cursor.close()
connection.close()
print("PostgreSQL connection is closed")
PostgreSQL server information
{'user': 'xoihbshcjgwwtd', 'dbname': 'd4oenn2g04nlqm', 'host': 'ec2-34-205-14-168.compute-1.amazonaws.com', 'port': '5432', 'tty': '', 'options': '', 'sslmode': 'prefer', 'sslcompression': '0', 'krbsrvname': 'postgres', 'target_session_attrs': 'any'}
You are connected to - ('PostgreSQL 13.4 (Ubuntu 13.4-4.pgdg20.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0, 64-bit',)
PostgreSQL connection is closed
Crear una tabla
El siguiente programa de Python se utilizará para crear una tabla en la base de datos previamente creada
import psycopg2
connection = psycopg2.connect(user="xoihbshcjgwwtd",
password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
host="ec2-34-205-14-168.compute-1.amazonaws.com",
port="5432",
database="d4oenn2g04nlqm")
cursor = connection.cursor()
cursor.execute('''DROP TABLE IF EXISTS company''')
cursor.execute('''CREATE TABLE company(
id SERIAL PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
age INT NOT NULL,
address CHAR(50),
salary REAL);
''')
print("Table created successfully")
connection.commit()
connection.close()
Table created successfully
Nótese en
pgAdminque la tabla fué creada exitosamente. También puede visualizar los cambios ocurridos en Heroku Postgres. Para visualizar la tabla enpgAdmindebe hacer click en el nombre de la tabla, luego en el boton con simbolo de tabla que aparece en la parte superiro izquierda.

Operación INSERT
El siguiente programa de
Pythonmuestra cómo podemos crear registros en nuestra tablacompanycreada en el ejemplo anterior
import psycopg2
connection = psycopg2.connect(user="xoihbshcjgwwtd",
password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
host="ec2-34-205-14-168.compute-1.amazonaws.com",
port="5432",
database="d4oenn2g04nlqm")
cursor = connection.cursor()
cursor.execute("INSERT INTO company (name, age, address, salary) VALUES ('Paul', 32, 'California', 20000.00 )");
cursor.execute("INSERT INTO company (name, age, address, salary) VALUES ('Allen', 25, 'Texas', 15000.00 )");
cursor.execute("INSERT INTO company (name, age, address, salary) VALUES ('Teddy', 23, 'Norway', 20000.00 )");
cursor.execute("INSERT INTO company (name, age, address, salary) \
VALUES ('Mark', 25, 'Rich-Mond ', 65000.00 )");
connection.commit()
print("Records created successfully")
connection.close()
Records created successfully
Observe desde
pgAdminque los registros han sido actualizados efectivamente, con los valores insertados en la tabla creada

Operación SELECT
El siguiente programa de
Pythonmuestra cómo podemos obtener y mostrar registros de nuestra tablacompanycreada en el ejemplo anterior
import psycopg2
connection = psycopg2.connect(user="xoihbshcjgwwtd",
password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
host="ec2-34-205-14-168.compute-1.amazonaws.com",
port="5432",
database="d4oenn2g04nlqm")
cursor = connection.cursor()
cursor.execute("SELECT id, name, address, salary from company")
rows = cursor.fetchall()
for row in rows:
print("id = ", row[0])
print("name = ", row[1])
print("address = ", row[2])
print("salary = ", row[3], "\n")
print("Operation done successfully")
connection.close()
id = 1
name = Paul
address = California
salary = 20000.0
id = 2
name = Allen
address = Texas
salary = 15000.0
id = 3
name = Teddy
address = Norway
salary = 20000.0
id = 4
name = Mark
address = Rich-Mond
salary = 65000.0
Operation done successfully
Operación UPDATE
El siguiente código de
Pythonmuestra cómo podemos utilizar la sentenciaUPDATEpara actualizar cualquier registro y luego obtener y mostrar los registros actualizados de nuestra tablacompany
import psycopg2
connection = psycopg2.connect(user="xoihbshcjgwwtd",
password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
host="ec2-34-205-14-168.compute-1.amazonaws.com",
port="5432",
database="d4oenn2g04nlqm")
cursor = connection.cursor()
cursor.execute("UPDATE company set salary = 25000.00 where id = 1")
connection.commit()
print("Total number of rows updated :", cursor.rowcount)
cursor.execute("SELECT id, name, address, salary from company")
rows = cursor.fetchall()
for row in rows:
print("id = ", row[0])
print("name = ", row[1])
print("address = ", row[2])
print("salary = ", row[3], "\n")
print("Operation done successfully")
connection.close()
Total number of rows updated : 1
id = 2
name = Allen
address = Texas
salary = 15000.0
id = 3
name = Teddy
address = Norway
salary = 20000.0
id = 4
name = Mark
address = Rich-Mond
salary = 65000.0
id = 1
name = Paul
address = California
salary = 25000.0
Operation done successfully
Operación DELETE
El siguiente código de
Pythonmuestra cómo podemos utilizar la sentenciaDELETEpara eliminar cualquier registro y luego obtener y mostrar los registros restantes de nuestra tablacompany
import psycopg2
connection = psycopg2.connect(user="xoihbshcjgwwtd",
password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
host="ec2-34-205-14-168.compute-1.amazonaws.com",
port="5432",
database="d4oenn2g04nlqm")
cursor = connection.cursor()
cursor.execute("DELETE from company where id = 2;")
connection.commit()
print("Total number of rows deleted :", cursor.rowcount)
cursor.execute("SELECT id, name, address, salary from company")
rows = cursor.fetchall()
for row in rows:
print("id = ", row[0])
print("name = ", row[1])
print("address = ", row[2])
print("salary = ", row[3], "\n")
print("Operation done successfully")
connection.close()
Total number of rows deleted : 1
id = 3
name = Teddy
address = Norway
salary = 20000.0
id = 4
name = Mark
address = Rich-Mond
salary = 65000.0
id = 1
name = Paul
address = California
salary = 25000.0
Operation done successfully
Importar un archivo CSV o un pandas DataFrame en PostgreSQL
En éste ejemplo vamos a importar un archivo
CSVenPostgreSQLutilizando la funciónto_sql()de la clasesqlalchemy. ElDataFramea usar corresponde al precio deEthereumenUSD. Vamos a importar el archivo en una tabla en nuestra base de datos y luego vamos a utilizar esta tabla para realizar un gráfico decandlesticks. Si desea ver los distinto tipos de variables numéricas y formatos de fechas enPostgreSQLver el siguiente par de links: Formatos numéricos, Formatos fechas
df_ETH = pd.read_csv("https://raw.githubusercontent.com/lihkir/Uninorte/main/AppliedStatisticMS/DataVisualizationRPython/Lectures/Python/PythonDataSets/ETH-USD.csv")
df_ETH.head()
| Date | Open | High | Low | Close | Adj Close | Volume | |
|---|---|---|---|---|---|---|---|
| 0 | 2015-08-07 | 2.831620 | 3.536610 | 2.521120 | 2.772120 | 2.772120 | 164329.0 |
| 1 | 2015-08-08 | 2.793760 | 2.798810 | 0.714725 | 0.753325 | 0.753325 | 674188.0 |
| 2 | 2015-08-09 | 0.706136 | 0.879810 | 0.629191 | 0.701897 | 0.701897 | 532170.0 |
| 3 | 2015-08-10 | 0.713989 | 0.729854 | 0.636546 | 0.708448 | 0.708448 | 405283.0 |
| 4 | 2015-08-11 | 0.708087 | 1.131410 | 0.663235 | 1.067860 | 1.067860 | 1463100.0 |
# from sqlalchemy import create_engine
# engine = create_engine('postgresql://xoihbshcjgwwtd:aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9@ec2-34-205-14-168.compute-1.amazonaws.com:5432/d4oenn2g04nlqm')
# df_ETH.to_sql('ethereum', engine, if_exists = 'replace', index=False)
# import psycopg2
# connection = psycopg2.connect(user="xoihbshcjgwwtd",
# password="aac4c765bf11a3982c859f19dcd57848c55b6e7b713fb98ca05e3711f2b9d7e9",
# host="ec2-34-205-14-168.compute-1.amazonaws.com",
# port="5432",
# database="d4oenn2g04nlqm")
# cursor = connection.cursor()
# cursor.execute("SELECT * from ethereum;")
# close = cursor.fetchall()
# close_df = pd.DataFrame(close)
# print("Operation done successfully")
# connection.close()
# close_df.columns = ["Date", "Open", "High", "Low", "Close", "Adj_Close", "Volume"]
# close_df = close_df.sort_values(by = "Date")
# close_df.head()
# import plotly.graph_objects as go
# fig = go.Figure(data=[go.Candlestick(x = close_df.date,
# open = close_df.open,
# high = close_df.high,
# low = close_df.low,
# close = close_df.close)
# ])
# fig.update_layout(
# title="Ethereum USD (ETH-USD)",
# xaxis_title="Day",
# yaxis_title="ETH-USD",
# font=dict(
# family="Courier New, monospace",
# size=12,
# color="RebeccaPurple"
# )
# )
# fig.update_layout(xaxis_rangeslider_visible=False)
# fig.show()
Tarea 2.3¶
La primera tarea es poner a funcionar todo lo realziado en esta sección y entenderlo. Luego de esto creará una nueva tabla que llamaremos
links, la cual contendrá información acerca de link de páginas web. La tabla debe ser creada siguiendo las siguientes instrucciones. En la siguiente revisaremos con detalle el significado de cada consultaSQL
CREATE TABLE links (
id SERIAL PRIMARY KEY,
url VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL,
description VARCHAR (255),
last_update DATE
);
Luego insertará datos a la tabla usando las siguientes ordenes. En cada caso mostrar debe mostrar el output generado por pgAdmin, así como también la actualización de Heroku Postgres
INSERT INTO links (url, name)
VALUES('https://www.postgresqltutorial.com','PostgreSQL Tutorial');
INSERT INTO links (url, name)
VALUES('http://www.oreilly.com','O''Reilly Media');
INSERT INTO links (url, name)
VALUES('http://www.oreilly.com','O''Reilly Media');
INSERT INTO links (url, name, last_update)
VALUES('https://www.google.com','Google','2013-06-01');
INSERT INTO links (url, name)
VALUES('http://www.postgresql.org','PostgreSQL')
RETURNING id;
Importe los datos del precio de Cardano USD (ADA-USD) en su base de datos Herokus Postgres, teniendo en cuenta lo explicado. Luego dibuje un gráfico de candlestick para la criptomoneda. En el siguiente link encontrará el CSV de Cardano: Cardano USD (ADA-USD)